mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-05 09:35:39 -05:00
Merge from vscode 8e0f348413f4f616c23a88ae30030efa85811973 (#6381)
* Merge from vscode 8e0f348413f4f616c23a88ae30030efa85811973 * disable strict null check
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IFileSystemProviderWithFileReadWriteCapability, IFileChange, IWatchOptions, IStat, FileOverwriteOptions, FileType, FileWriteOptions, FileDeleteOptions, FileSystemProviderCapabilities, IFileSystemProviderWithOpenReadWriteCloseCapability, FileOpenOptions, hasReadWriteCapability, hasOpenReadWriteCloseCapability } from 'vs/platform/files/common/files';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import * as resources from 'vs/base/common/resources';
|
||||
import { startsWith } from 'vs/base/common/strings';
|
||||
import { BACKUPS } from 'vs/platform/environment/common/environment';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
|
||||
export class FileUserDataProvider extends Disposable implements IFileSystemProviderWithFileReadWriteCapability, IFileSystemProviderWithOpenReadWriteCloseCapability {
|
||||
|
||||
readonly capabilities: FileSystemProviderCapabilities = this.fileSystemProvider.capabilities;
|
||||
readonly onDidChangeCapabilities: Event<void> = Event.None;
|
||||
|
||||
private readonly _onDidChangeFile: Emitter<IFileChange[]> = this._register(new Emitter<IFileChange[]>());
|
||||
readonly onDidChangeFile: Event<IFileChange[]> = this._onDidChangeFile.event;
|
||||
|
||||
private readonly userDataHome: URI;
|
||||
|
||||
constructor(
|
||||
private readonly fileSystemUserDataHome: URI,
|
||||
private readonly fileSystemBackupsHome: URI,
|
||||
private readonly fileSystemProvider: IFileSystemProviderWithFileReadWriteCapability | IFileSystemProviderWithOpenReadWriteCloseCapability,
|
||||
environmentService: IWorkbenchEnvironmentService
|
||||
) {
|
||||
super();
|
||||
|
||||
this.userDataHome = environmentService.userRoamingDataHome;
|
||||
|
||||
// Assumption: This path always exists
|
||||
this._register(this.fileSystemProvider.watch(this.fileSystemUserDataHome, { recursive: false, excludes: [] }));
|
||||
this._register(this.fileSystemProvider.onDidChangeFile(e => this.handleFileChanges(e)));
|
||||
}
|
||||
|
||||
watch(resource: URI, opts: IWatchOptions): IDisposable {
|
||||
return this.fileSystemProvider.watch(this.toFileSystemResource(resource), opts);
|
||||
}
|
||||
|
||||
stat(resource: URI): Promise<IStat> {
|
||||
return this.fileSystemProvider.stat(this.toFileSystemResource(resource));
|
||||
}
|
||||
|
||||
mkdir(resource: URI): Promise<void> {
|
||||
return this.fileSystemProvider.mkdir(this.toFileSystemResource(resource));
|
||||
}
|
||||
|
||||
rename(from: URI, to: URI, opts: FileOverwriteOptions): Promise<void> {
|
||||
return this.fileSystemProvider.rename(this.toFileSystemResource(from), this.toFileSystemResource(to), opts);
|
||||
}
|
||||
|
||||
readFile(resource: URI): Promise<Uint8Array> {
|
||||
if (hasReadWriteCapability(this.fileSystemProvider)) {
|
||||
return this.fileSystemProvider.readFile(this.toFileSystemResource(resource));
|
||||
}
|
||||
throw new Error('not supported');
|
||||
}
|
||||
|
||||
readdir(resource: URI): Promise<[string, FileType][]> {
|
||||
return this.fileSystemProvider.readdir(this.toFileSystemResource(resource));
|
||||
}
|
||||
|
||||
writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise<void> {
|
||||
if (hasReadWriteCapability(this.fileSystemProvider)) {
|
||||
return this.fileSystemProvider.writeFile(this.toFileSystemResource(resource), content, opts);
|
||||
}
|
||||
throw new Error('not supported');
|
||||
}
|
||||
|
||||
open(resource: URI, opts: FileOpenOptions): Promise<number> {
|
||||
if (hasOpenReadWriteCloseCapability(this.fileSystemProvider)) {
|
||||
return this.fileSystemProvider.open(this.toFileSystemResource(resource), opts);
|
||||
}
|
||||
throw new Error('not supported');
|
||||
}
|
||||
|
||||
close(fd: number): Promise<void> {
|
||||
if (hasOpenReadWriteCloseCapability(this.fileSystemProvider)) {
|
||||
return this.fileSystemProvider.close(fd);
|
||||
}
|
||||
throw new Error('not supported');
|
||||
}
|
||||
|
||||
read(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise<number> {
|
||||
if (hasOpenReadWriteCloseCapability(this.fileSystemProvider)) {
|
||||
return this.fileSystemProvider.read(fd, pos, data, offset, length);
|
||||
}
|
||||
throw new Error('not supported');
|
||||
}
|
||||
|
||||
write(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise<number> {
|
||||
if (hasOpenReadWriteCloseCapability(this.fileSystemProvider)) {
|
||||
return this.fileSystemProvider.write(fd, pos, data, offset, length);
|
||||
}
|
||||
throw new Error('not supported');
|
||||
}
|
||||
|
||||
delete(resource: URI, opts: FileDeleteOptions): Promise<void> {
|
||||
return this.fileSystemProvider.delete(this.toFileSystemResource(resource), opts);
|
||||
}
|
||||
|
||||
private handleFileChanges(changes: IFileChange[]): void {
|
||||
const userDataChanges: IFileChange[] = [];
|
||||
for (const change of changes) {
|
||||
const userDataResource = this.toUserDataResource(change.resource);
|
||||
if (userDataResource) {
|
||||
userDataChanges.push({
|
||||
resource: userDataResource,
|
||||
type: change.type
|
||||
});
|
||||
}
|
||||
}
|
||||
if (userDataChanges.length) {
|
||||
this._onDidChangeFile.fire(userDataChanges);
|
||||
}
|
||||
}
|
||||
|
||||
private toFileSystemResource(userDataResource: URI): URI {
|
||||
const relativePath = resources.relativePath(this.userDataHome, userDataResource)!;
|
||||
if (startsWith(relativePath, BACKUPS)) {
|
||||
return resources.joinPath(resources.dirname(this.fileSystemBackupsHome), relativePath);
|
||||
}
|
||||
return resources.joinPath(this.fileSystemUserDataHome, relativePath);
|
||||
}
|
||||
|
||||
private toUserDataResource(fileSystemResource: URI): URI | null {
|
||||
if (resources.isEqualOrParent(fileSystemResource, this.fileSystemUserDataHome)) {
|
||||
const relativePath = resources.relativePath(this.fileSystemUserDataHome, fileSystemResource);
|
||||
return relativePath ? resources.joinPath(this.userDataHome, relativePath) : this.userDataHome;
|
||||
}
|
||||
if (resources.isEqualOrParent(fileSystemResource, this.fileSystemBackupsHome)) {
|
||||
const relativePath = resources.relativePath(this.fileSystemBackupsHome, fileSystemResource);
|
||||
return relativePath ? resources.joinPath(this.userDataHome, BACKUPS, relativePath) : resources.joinPath(this.userDataHome, BACKUPS);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import * as resources from 'vs/base/common/resources';
|
||||
import { FileChangeType, IFileSystemProvider, FileType, IWatchOptions, IStat, FileSystemProviderErrorCode, FileSystemProviderError, FileWriteOptions, IFileChange, FileDeleteOptions, FileSystemProviderCapabilities, FileOverwriteOptions } from 'vs/platform/files/common/files';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
||||
class File implements IStat {
|
||||
|
||||
type: FileType;
|
||||
ctime: number;
|
||||
mtime: number;
|
||||
size: number;
|
||||
|
||||
name: string;
|
||||
data?: Uint8Array;
|
||||
|
||||
constructor(name: string) {
|
||||
this.type = FileType.File;
|
||||
this.ctime = Date.now();
|
||||
this.mtime = Date.now();
|
||||
this.size = 0;
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
class Directory implements IStat {
|
||||
|
||||
type: FileType;
|
||||
ctime: number;
|
||||
mtime: number;
|
||||
size: number;
|
||||
|
||||
name: string;
|
||||
entries: Map<string, File | Directory>;
|
||||
|
||||
constructor(name: string) {
|
||||
this.type = FileType.Directory;
|
||||
this.ctime = Date.now();
|
||||
this.mtime = Date.now();
|
||||
this.size = 0;
|
||||
this.name = name;
|
||||
this.entries = new Map();
|
||||
}
|
||||
}
|
||||
|
||||
export type Entry = File | Directory;
|
||||
|
||||
export class InMemoryUserDataProvider extends Disposable implements IFileSystemProvider {
|
||||
|
||||
readonly capabilities: FileSystemProviderCapabilities = FileSystemProviderCapabilities.FileReadWrite;
|
||||
readonly onDidChangeCapabilities: Event<void> = Event.None;
|
||||
|
||||
root = new Directory('');
|
||||
|
||||
// --- manage file metadata
|
||||
|
||||
async stat(resource: URI): Promise<IStat> {
|
||||
return this._lookup(resource, false);
|
||||
}
|
||||
|
||||
async readdir(resource: URI): Promise<[string, FileType][]> {
|
||||
const entry = this._lookupAsDirectory(resource, false);
|
||||
let result: [string, FileType][] = [];
|
||||
for (const [name, child] of entry.entries) {
|
||||
result.push([name, child.type]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// --- manage file contents
|
||||
|
||||
async readFile(resource: URI): Promise<Uint8Array> {
|
||||
const data = this._lookupAsFile(resource, false).data;
|
||||
if (data) {
|
||||
return data;
|
||||
}
|
||||
throw new FileSystemProviderError('file not found', FileSystemProviderErrorCode.FileNotFound);
|
||||
}
|
||||
|
||||
async writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise<void> {
|
||||
let basename = resources.basename(resource);
|
||||
let parent = this._lookupParentDirectory(resource);
|
||||
let entry = parent.entries.get(basename);
|
||||
if (entry instanceof Directory) {
|
||||
throw new FileSystemProviderError('file is directory', FileSystemProviderErrorCode.FileIsADirectory);
|
||||
}
|
||||
if (!entry && !opts.create) {
|
||||
throw new FileSystemProviderError('file not found', FileSystemProviderErrorCode.FileNotFound);
|
||||
}
|
||||
if (entry && opts.create && !opts.overwrite) {
|
||||
throw new FileSystemProviderError('file exists already', FileSystemProviderErrorCode.FileExists);
|
||||
}
|
||||
if (!entry) {
|
||||
entry = new File(basename);
|
||||
parent.entries.set(basename, entry);
|
||||
this._fireSoon({ type: FileChangeType.ADDED, resource });
|
||||
}
|
||||
entry.mtime = Date.now();
|
||||
entry.size = content.byteLength;
|
||||
entry.data = content;
|
||||
|
||||
this._fireSoon({ type: FileChangeType.UPDATED, resource });
|
||||
}
|
||||
|
||||
// --- manage files/folders
|
||||
|
||||
async rename(from: URI, to: URI, opts: FileOverwriteOptions): Promise<void> {
|
||||
if (!opts.overwrite && this._lookup(to, true)) {
|
||||
throw new FileSystemProviderError('file exists already', FileSystemProviderErrorCode.FileExists);
|
||||
}
|
||||
|
||||
let entry = this._lookup(from, false);
|
||||
let oldParent = this._lookupParentDirectory(from);
|
||||
|
||||
let newParent = this._lookupParentDirectory(to);
|
||||
let newName = resources.basename(to);
|
||||
|
||||
oldParent.entries.delete(entry.name);
|
||||
entry.name = newName;
|
||||
newParent.entries.set(newName, entry);
|
||||
|
||||
this._fireSoon(
|
||||
{ type: FileChangeType.DELETED, resource: from },
|
||||
{ type: FileChangeType.ADDED, resource: to }
|
||||
);
|
||||
}
|
||||
|
||||
async delete(resource: URI, opts: FileDeleteOptions): Promise<void> {
|
||||
let dirname = resources.dirname(resource);
|
||||
let basename = resources.basename(resource);
|
||||
let parent = this._lookupAsDirectory(dirname, false);
|
||||
if (!parent.entries.has(basename)) {
|
||||
throw new FileSystemProviderError('file not found', FileSystemProviderErrorCode.FileNotFound);
|
||||
}
|
||||
parent.entries.delete(basename);
|
||||
parent.mtime = Date.now();
|
||||
parent.size -= 1;
|
||||
this._fireSoon({ type: FileChangeType.UPDATED, resource: dirname }, { resource, type: FileChangeType.DELETED });
|
||||
}
|
||||
|
||||
async mkdir(resource: URI): Promise<void> {
|
||||
let basename = resources.basename(resource);
|
||||
let dirname = resources.dirname(resource);
|
||||
let parent = this._lookupAsDirectory(dirname, false);
|
||||
|
||||
let entry = new Directory(basename);
|
||||
parent.entries.set(entry.name, entry);
|
||||
parent.mtime = Date.now();
|
||||
parent.size += 1;
|
||||
this._fireSoon({ type: FileChangeType.UPDATED, resource: dirname }, { type: FileChangeType.ADDED, resource });
|
||||
}
|
||||
|
||||
// --- lookup
|
||||
|
||||
private _lookup(uri: URI, silent: false): Entry;
|
||||
private _lookup(uri: URI, silent: boolean): Entry | undefined;
|
||||
private _lookup(uri: URI, silent: boolean): Entry | undefined {
|
||||
let parts = uri.path.split('/');
|
||||
let entry: Entry = this.root;
|
||||
for (const part of parts) {
|
||||
if (!part) {
|
||||
continue;
|
||||
}
|
||||
let child: Entry | undefined;
|
||||
if (entry instanceof Directory) {
|
||||
child = entry.entries.get(part);
|
||||
}
|
||||
if (!child) {
|
||||
if (!silent) {
|
||||
throw new FileSystemProviderError('file not found', FileSystemProviderErrorCode.FileNotFound);
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
entry = child;
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
private _lookupAsDirectory(uri: URI, silent: boolean): Directory {
|
||||
let entry = this._lookup(uri, silent);
|
||||
if (entry instanceof Directory) {
|
||||
return entry;
|
||||
}
|
||||
throw new FileSystemProviderError('file not a directory', FileSystemProviderErrorCode.FileNotADirectory);
|
||||
}
|
||||
|
||||
private _lookupAsFile(uri: URI, silent: boolean): File {
|
||||
let entry = this._lookup(uri, silent);
|
||||
if (entry instanceof File) {
|
||||
return entry;
|
||||
}
|
||||
throw new FileSystemProviderError('file is a directory', FileSystemProviderErrorCode.FileIsADirectory);
|
||||
}
|
||||
|
||||
private _lookupParentDirectory(uri: URI): Directory {
|
||||
const dirname = resources.dirname(uri);
|
||||
return this._lookupAsDirectory(dirname, false);
|
||||
}
|
||||
|
||||
// --- manage file events
|
||||
|
||||
private readonly _onDidChangeFile: Emitter<IFileChange[]> = this._register(new Emitter<IFileChange[]>());
|
||||
readonly onDidChangeFile: Event<IFileChange[]> = this._onDidChangeFile.event;
|
||||
|
||||
private _bufferedChanges: IFileChange[] = [];
|
||||
private _fireSoonHandle?: NodeJS.Timer;
|
||||
|
||||
|
||||
watch(resource: URI, opts: IWatchOptions): IDisposable {
|
||||
// ignore, fires for all changes...
|
||||
return Disposable.None;
|
||||
}
|
||||
|
||||
private _fireSoon(...changes: IFileChange[]): void {
|
||||
this._bufferedChanges.push(...changes);
|
||||
|
||||
if (this._fireSoonHandle) {
|
||||
clearTimeout(this._fireSoonHandle);
|
||||
}
|
||||
|
||||
this._fireSoonHandle = setTimeout(() => {
|
||||
this._onDidChangeFile.fire(this._bufferedChanges);
|
||||
this._bufferedChanges.length = 0;
|
||||
}, 5);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,478 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 * as os from 'os';
|
||||
import * as path from 'vs/base/common/path';
|
||||
import * as uuid from 'vs/base/common/uuid';
|
||||
import * as pfs from 'vs/base/node/pfs';
|
||||
import { IFileService, FileChangeType, IFileChange, IFileSystemProviderWithFileReadWriteCapability, IStat, FileType, FileSystemProviderCapabilities } from 'vs/platform/files/common/files';
|
||||
import { FileService } from 'vs/platform/files/common/fileService';
|
||||
import { NullLogService } from 'vs/platform/log/common/log';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { FileUserDataProvider } from 'vs/workbench/services/userData/common/fileUserDataProvider';
|
||||
import { joinPath, dirname } from 'vs/base/common/resources';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { DiskFileSystemProvider } from 'vs/platform/files/electron-browser/diskFileSystemProvider';
|
||||
import { BACKUPS } from 'vs/platform/environment/common/environment';
|
||||
import { DisposableStore, IDisposable, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { BrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
|
||||
suite('FileUserDataProvider', () => {
|
||||
|
||||
let testObject: IFileService;
|
||||
let rootPath: string;
|
||||
let userDataPath: string;
|
||||
let backupsPath: string;
|
||||
let userDataResource: URI;
|
||||
const disposables = new DisposableStore();
|
||||
|
||||
setup(async () => {
|
||||
const logService = new NullLogService();
|
||||
testObject = new FileService(logService);
|
||||
disposables.add(testObject);
|
||||
|
||||
const diskFileSystemProvider = new DiskFileSystemProvider(logService);
|
||||
disposables.add(diskFileSystemProvider);
|
||||
disposables.add(testObject.registerProvider(Schemas.file, diskFileSystemProvider));
|
||||
|
||||
rootPath = path.join(os.tmpdir(), 'vsctests', uuid.generateUuid());
|
||||
userDataPath = path.join(rootPath, 'user');
|
||||
backupsPath = path.join(rootPath, BACKUPS);
|
||||
userDataResource = URI.file(userDataPath).with({ scheme: Schemas.userData });
|
||||
await Promise.all([pfs.mkdirp(userDataPath), pfs.mkdirp(backupsPath)]);
|
||||
|
||||
const environmentService = new BrowserWorkbenchEnvironmentService({ workspaceId: 'workspaceId' });
|
||||
environmentService.userRoamingDataHome = userDataResource;
|
||||
|
||||
const userDataFileSystemProvider = new FileUserDataProvider(URI.file(userDataPath), URI.file(backupsPath), diskFileSystemProvider, environmentService);
|
||||
disposables.add(userDataFileSystemProvider);
|
||||
disposables.add(testObject.registerProvider(Schemas.userData, userDataFileSystemProvider));
|
||||
});
|
||||
|
||||
teardown(async () => {
|
||||
disposables.clear();
|
||||
await pfs.rimraf(rootPath, pfs.RimRafMode.MOVE);
|
||||
});
|
||||
|
||||
test('exists return false when file does not exist', async () => {
|
||||
const exists = await testObject.exists(joinPath(userDataResource, 'settings.json'));
|
||||
assert.equal(exists, false);
|
||||
});
|
||||
|
||||
test('read file throws error if not exist', async () => {
|
||||
try {
|
||||
await testObject.readFile(joinPath(userDataResource, 'settings.json'));
|
||||
assert.fail('Should fail since file does not exist');
|
||||
} catch (e) { }
|
||||
});
|
||||
|
||||
test('read existing file', async () => {
|
||||
await pfs.writeFile(path.join(userDataPath, 'settings.json'), '{}');
|
||||
const result = await testObject.readFile(joinPath(userDataResource, 'settings.json'));
|
||||
assert.equal(result.value, '{}');
|
||||
});
|
||||
|
||||
test('create file', async () => {
|
||||
const resource = joinPath(userDataResource, 'settings.json');
|
||||
const actual1 = await testObject.createFile(resource, VSBuffer.fromString('{}'));
|
||||
assert.equal(actual1.resource.toString(), resource.toString());
|
||||
const actual2 = await pfs.readFile(path.join(userDataPath, 'settings.json'));
|
||||
assert.equal(actual2, '{}');
|
||||
});
|
||||
|
||||
test('write file creates the file if not exist', async () => {
|
||||
const resource = joinPath(userDataResource, 'settings.json');
|
||||
const actual1 = await testObject.writeFile(resource, VSBuffer.fromString('{}'));
|
||||
assert.equal(actual1.resource.toString(), resource.toString());
|
||||
const actual2 = await pfs.readFile(path.join(userDataPath, 'settings.json'));
|
||||
assert.equal(actual2, '{}');
|
||||
});
|
||||
|
||||
test('write to existing file', async () => {
|
||||
const resource = joinPath(userDataResource, 'settings.json');
|
||||
await pfs.writeFile(path.join(userDataPath, 'settings.json'), '{}');
|
||||
const actual1 = await testObject.writeFile(resource, VSBuffer.fromString('{a:1}'));
|
||||
assert.equal(actual1.resource.toString(), resource.toString());
|
||||
const actual2 = await pfs.readFile(path.join(userDataPath, 'settings.json'));
|
||||
assert.equal(actual2, '{a:1}');
|
||||
});
|
||||
|
||||
test('delete file', async () => {
|
||||
await pfs.writeFile(path.join(userDataPath, 'settings.json'), '');
|
||||
await testObject.del(joinPath(userDataResource, 'settings.json'));
|
||||
const result = await pfs.exists(path.join(userDataPath, 'settings.json'));
|
||||
assert.equal(false, result);
|
||||
});
|
||||
|
||||
test('resolve file', async () => {
|
||||
await pfs.writeFile(path.join(userDataPath, 'settings.json'), '');
|
||||
const result = await testObject.resolve(joinPath(userDataResource, 'settings.json'));
|
||||
assert.ok(!result.isDirectory);
|
||||
assert.ok(result.children === undefined);
|
||||
});
|
||||
|
||||
test('exists return false for folder that does not exist', async () => {
|
||||
const exists = await testObject.exists(joinPath(userDataResource, 'snippets'));
|
||||
assert.equal(exists, false);
|
||||
});
|
||||
|
||||
test('exists return true for folder that exists', async () => {
|
||||
await pfs.mkdirp(path.join(userDataPath, 'snippets'));
|
||||
const exists = await testObject.exists(joinPath(userDataResource, 'snippets'));
|
||||
assert.equal(exists, true);
|
||||
});
|
||||
|
||||
test('read file throws error for folder', async () => {
|
||||
await pfs.mkdirp(path.join(userDataPath, 'snippets'));
|
||||
try {
|
||||
await testObject.readFile(joinPath(userDataResource, 'snippets'));
|
||||
assert.fail('Should fail since read file is not supported for folders');
|
||||
} catch (e) { }
|
||||
});
|
||||
|
||||
test('read file under folder', async () => {
|
||||
await pfs.mkdirp(path.join(userDataPath, 'snippets'));
|
||||
await pfs.writeFile(path.join(userDataPath, 'snippets', 'settings.json'), '{}');
|
||||
const resource = joinPath(userDataResource, 'snippets/settings.json');
|
||||
const actual = await testObject.readFile(resource);
|
||||
assert.equal(actual.resource.toString(), resource.toString());
|
||||
assert.equal(actual.value, '{}');
|
||||
});
|
||||
|
||||
test('read file under sub folder', async () => {
|
||||
await pfs.mkdirp(path.join(userDataPath, 'snippets', 'java'));
|
||||
await pfs.writeFile(path.join(userDataPath, 'snippets', 'java', 'settings.json'), '{}');
|
||||
const resource = joinPath(userDataResource, 'snippets/java/settings.json');
|
||||
const actual = await testObject.readFile(resource);
|
||||
assert.equal(actual.resource.toString(), resource.toString());
|
||||
assert.equal(actual.value, '{}');
|
||||
});
|
||||
|
||||
test('create file under folder that exists', async () => {
|
||||
await pfs.mkdirp(path.join(userDataPath, 'snippets'));
|
||||
const resource = joinPath(userDataResource, 'snippets/settings.json');
|
||||
const actual1 = await testObject.createFile(resource, VSBuffer.fromString('{}'));
|
||||
assert.equal(actual1.resource.toString(), resource.toString());
|
||||
const actual2 = await pfs.readFile(path.join(userDataPath, 'snippets', 'settings.json'));
|
||||
assert.equal(actual2, '{}');
|
||||
});
|
||||
|
||||
test('create file under folder that does not exist', async () => {
|
||||
const resource = joinPath(userDataResource, 'snippets/settings.json');
|
||||
const actual1 = await testObject.createFile(resource, VSBuffer.fromString('{}'));
|
||||
assert.equal(actual1.resource.toString(), resource.toString());
|
||||
const actual2 = await pfs.readFile(path.join(userDataPath, 'snippets', 'settings.json'));
|
||||
assert.equal(actual2, '{}');
|
||||
});
|
||||
|
||||
test('write to not existing file under container that exists', async () => {
|
||||
await pfs.mkdirp(path.join(userDataPath, 'snippets'));
|
||||
const resource = joinPath(userDataResource, 'snippets/settings.json');
|
||||
const actual1 = await testObject.writeFile(resource, VSBuffer.fromString('{}'));
|
||||
assert.equal(actual1.resource.toString(), resource.toString());
|
||||
const actual = await pfs.readFile(path.join(userDataPath, 'snippets', 'settings.json'));
|
||||
assert.equal(actual, '{}');
|
||||
});
|
||||
|
||||
test('write to not existing file under container that does not exists', async () => {
|
||||
const resource = joinPath(userDataResource, 'snippets/settings.json');
|
||||
const actual1 = await testObject.writeFile(resource, VSBuffer.fromString('{}'));
|
||||
assert.equal(actual1.resource.toString(), resource.toString());
|
||||
const actual = await pfs.readFile(path.join(userDataPath, 'snippets', 'settings.json'));
|
||||
assert.equal(actual, '{}');
|
||||
});
|
||||
|
||||
test('write to existing file under container', async () => {
|
||||
await pfs.mkdirp(path.join(userDataPath, 'snippets'));
|
||||
await pfs.writeFile(path.join(userDataPath, 'snippets', 'settings.json'), '{}');
|
||||
const resource = joinPath(userDataResource, 'snippets/settings.json');
|
||||
const actual1 = await testObject.writeFile(resource, VSBuffer.fromString('{a:1}'));
|
||||
assert.equal(actual1.resource.toString(), resource.toString());
|
||||
const actual = await pfs.readFile(path.join(userDataPath, 'snippets', 'settings.json'));
|
||||
assert.equal(actual.toString(), '{a:1}');
|
||||
});
|
||||
|
||||
test('write file under sub container', async () => {
|
||||
const resource = joinPath(userDataResource, 'snippets/java/settings.json');
|
||||
const actual1 = await testObject.writeFile(resource, VSBuffer.fromString('{}'));
|
||||
assert.equal(actual1.resource.toString(), resource.toString());
|
||||
const actual = await pfs.readFile(path.join(userDataPath, 'snippets', 'java', 'settings.json'));
|
||||
assert.equal(actual, '{}');
|
||||
});
|
||||
|
||||
test('delete throws error for folder that does not exist', async () => {
|
||||
try {
|
||||
await testObject.del(joinPath(userDataResource, 'snippets'));
|
||||
assert.fail('Should fail the folder does not exist');
|
||||
} catch (e) { }
|
||||
});
|
||||
|
||||
test('delete not existing file under container that exists', async () => {
|
||||
await pfs.mkdirp(path.join(userDataPath, 'snippets'));
|
||||
try {
|
||||
await testObject.del(joinPath(userDataResource, 'snippets/settings.json'));
|
||||
assert.fail('Should fail since file does not exist');
|
||||
} catch (e) { }
|
||||
});
|
||||
|
||||
test('delete not existing file under container that does not exists', async () => {
|
||||
try {
|
||||
await testObject.del(joinPath(userDataResource, 'snippets/settings.json'));
|
||||
assert.fail('Should fail since file does not exist');
|
||||
} catch (e) { }
|
||||
});
|
||||
|
||||
test('delete existing file under folder', async () => {
|
||||
await pfs.mkdirp(path.join(userDataPath, 'snippets'));
|
||||
await pfs.writeFile(path.join(userDataPath, 'snippets', 'settings.json'), '{}');
|
||||
await testObject.del(joinPath(userDataResource, 'snippets/settings.json'));
|
||||
const exists = await pfs.exists(path.join(userDataPath, 'snippets', 'settings.json'));
|
||||
assert.equal(exists, false);
|
||||
});
|
||||
|
||||
test('resolve folder', async () => {
|
||||
await pfs.mkdirp(path.join(userDataPath, 'snippets'));
|
||||
await pfs.writeFile(path.join(userDataPath, 'snippets', 'settings.json'), '{}');
|
||||
const result = await testObject.resolve(joinPath(userDataResource, 'snippets'));
|
||||
assert.ok(result.isDirectory);
|
||||
assert.ok(result.children !== undefined);
|
||||
assert.equal(result.children!.length, 1);
|
||||
assert.equal(result.children![0].resource.toString(), joinPath(userDataResource, 'snippets/settings.json').toString());
|
||||
});
|
||||
|
||||
test('read backup file', async () => {
|
||||
await pfs.writeFile(path.join(backupsPath, 'backup.json'), '{}');
|
||||
const result = await testObject.readFile(joinPath(userDataResource, `${BACKUPS}/backup.json`));
|
||||
assert.equal(result.value, '{}');
|
||||
});
|
||||
|
||||
test('create backup file', async () => {
|
||||
await testObject.createFile(joinPath(userDataResource, `${BACKUPS}/backup.json`), VSBuffer.fromString('{}'));
|
||||
const result = await pfs.readFile(path.join(backupsPath, 'backup.json'));
|
||||
assert.equal(result, '{}');
|
||||
});
|
||||
|
||||
test('write backup file', async () => {
|
||||
await pfs.writeFile(path.join(backupsPath, 'backup.json'), '{}');
|
||||
await testObject.writeFile(joinPath(userDataResource, `${BACKUPS}/backup.json`), VSBuffer.fromString('{a:1}'));
|
||||
const result = await pfs.readFile(path.join(backupsPath, 'backup.json'));
|
||||
assert.equal(result, '{a:1}');
|
||||
});
|
||||
|
||||
test('resolve backups folder', async () => {
|
||||
await pfs.writeFile(path.join(backupsPath, 'backup.json'), '{}');
|
||||
const result = await testObject.resolve(joinPath(userDataResource, BACKUPS));
|
||||
assert.ok(result.isDirectory);
|
||||
assert.ok(result.children !== undefined);
|
||||
assert.equal(result.children!.length, 1);
|
||||
assert.equal(result.children![0].resource.toString(), joinPath(userDataResource, `${BACKUPS}/backup.json`).toString());
|
||||
});
|
||||
});
|
||||
|
||||
class TestFileSystemProvider implements IFileSystemProviderWithFileReadWriteCapability {
|
||||
|
||||
constructor(readonly onDidChangeFile: Event<IFileChange[]>) { }
|
||||
|
||||
readonly capabilities: FileSystemProviderCapabilities = FileSystemProviderCapabilities.FileReadWrite;
|
||||
|
||||
readonly onDidChangeCapabilities: Event<void> = Event.None;
|
||||
|
||||
watch(): IDisposable { return Disposable.None; }
|
||||
|
||||
stat(): Promise<IStat> { throw new Error('Not Supported'); }
|
||||
|
||||
mkdir(resource: URI): Promise<void> { throw new Error('Not Supported'); }
|
||||
|
||||
rename(): Promise<void> { throw new Error('Not Supported'); }
|
||||
|
||||
readFile(resource: URI): Promise<Uint8Array> { throw new Error('Not Supported'); }
|
||||
|
||||
readdir(resource: URI): Promise<[string, FileType][]> { throw new Error('Not Supported'); }
|
||||
|
||||
writeFile(): Promise<void> { throw new Error('Not Supported'); }
|
||||
|
||||
delete(): Promise<void> { throw new Error('Not Supported'); }
|
||||
|
||||
}
|
||||
|
||||
suite('FileUserDataProvider - Watching', () => {
|
||||
|
||||
let testObject: IFileService;
|
||||
let localBackupsResource: URI;
|
||||
let localUserDataResource: URI;
|
||||
let userDataResource: URI;
|
||||
const disposables = new DisposableStore();
|
||||
|
||||
const fileEventEmitter: Emitter<IFileChange[]> = new Emitter<IFileChange[]>();
|
||||
disposables.add(fileEventEmitter);
|
||||
|
||||
setup(() => {
|
||||
|
||||
const rootPath = path.join(os.tmpdir(), 'vsctests', uuid.generateUuid());
|
||||
const userDataPath = path.join(rootPath, 'user');
|
||||
const backupsPath = path.join(rootPath, BACKUPS);
|
||||
localBackupsResource = URI.file(backupsPath);
|
||||
localUserDataResource = URI.file(userDataPath);
|
||||
userDataResource = localUserDataResource.with({ scheme: Schemas.userData });
|
||||
|
||||
const environmentService = new BrowserWorkbenchEnvironmentService({ workspaceId: 'workspaceId' });
|
||||
environmentService.userRoamingDataHome = userDataResource;
|
||||
|
||||
const userDataFileSystemProvider = new FileUserDataProvider(localUserDataResource, localBackupsResource, new TestFileSystemProvider(fileEventEmitter.event), environmentService);
|
||||
disposables.add(userDataFileSystemProvider);
|
||||
|
||||
testObject = new FileService(new NullLogService());
|
||||
disposables.add(testObject);
|
||||
disposables.add(testObject.registerProvider(Schemas.userData, userDataFileSystemProvider));
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
disposables.clear();
|
||||
});
|
||||
|
||||
test('file added change event', done => {
|
||||
const expected = joinPath(userDataResource, 'settings.json');
|
||||
const target = joinPath(localUserDataResource, 'settings.json');
|
||||
testObject.onFileChanges(e => {
|
||||
if (e.contains(expected, FileChangeType.ADDED)) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
fileEventEmitter.fire([{
|
||||
resource: target,
|
||||
type: FileChangeType.ADDED
|
||||
}]);
|
||||
});
|
||||
|
||||
test('file updated change event', done => {
|
||||
const expected = joinPath(userDataResource, 'settings.json');
|
||||
const target = joinPath(localUserDataResource, 'settings.json');
|
||||
testObject.onFileChanges(e => {
|
||||
if (e.contains(expected, FileChangeType.UPDATED)) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
fileEventEmitter.fire([{
|
||||
resource: target,
|
||||
type: FileChangeType.UPDATED
|
||||
}]);
|
||||
});
|
||||
|
||||
test('file deleted change event', done => {
|
||||
const expected = joinPath(userDataResource, 'settings.json');
|
||||
const target = joinPath(localUserDataResource, 'settings.json');
|
||||
testObject.onFileChanges(e => {
|
||||
if (e.contains(expected, FileChangeType.DELETED)) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
fileEventEmitter.fire([{
|
||||
resource: target,
|
||||
type: FileChangeType.DELETED
|
||||
}]);
|
||||
});
|
||||
|
||||
test('file under folder created change event', done => {
|
||||
const expected = joinPath(userDataResource, 'snippets', 'settings.json');
|
||||
const target = joinPath(localUserDataResource, 'snippets', 'settings.json');
|
||||
testObject.onFileChanges(e => {
|
||||
if (e.contains(expected, FileChangeType.ADDED)) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
fileEventEmitter.fire([{
|
||||
resource: target,
|
||||
type: FileChangeType.ADDED
|
||||
}]);
|
||||
});
|
||||
|
||||
test('file under folder updated change event', done => {
|
||||
const expected = joinPath(userDataResource, 'snippets', 'settings.json');
|
||||
const target = joinPath(localUserDataResource, 'snippets', 'settings.json');
|
||||
testObject.onFileChanges(e => {
|
||||
if (e.contains(expected, FileChangeType.UPDATED)) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
fileEventEmitter.fire([{
|
||||
resource: target,
|
||||
type: FileChangeType.UPDATED
|
||||
}]);
|
||||
});
|
||||
|
||||
test('file under folder deleted change event', done => {
|
||||
const expected = joinPath(userDataResource, 'snippets', 'settings.json');
|
||||
const target = joinPath(localUserDataResource, 'snippets', 'settings.json');
|
||||
testObject.onFileChanges(e => {
|
||||
if (e.contains(expected, FileChangeType.DELETED)) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
fileEventEmitter.fire([{
|
||||
resource: target,
|
||||
type: FileChangeType.DELETED
|
||||
}]);
|
||||
});
|
||||
|
||||
test('event is not triggered if file is not under user data', async () => {
|
||||
const target = joinPath(dirname(localUserDataResource), 'settings.json');
|
||||
let triggered = false;
|
||||
testObject.onFileChanges(() => triggered = true);
|
||||
fileEventEmitter.fire([{
|
||||
resource: target,
|
||||
type: FileChangeType.DELETED
|
||||
}]);
|
||||
await timeout(0);
|
||||
if (triggered) {
|
||||
assert.fail('event should not be triggered');
|
||||
}
|
||||
});
|
||||
|
||||
test('backup file created change event', done => {
|
||||
const expected = joinPath(userDataResource, BACKUPS, 'settings.json');
|
||||
const target = joinPath(localBackupsResource, 'settings.json');
|
||||
testObject.onFileChanges(e => {
|
||||
if (e.contains(expected, FileChangeType.ADDED)) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
fileEventEmitter.fire([{
|
||||
resource: target,
|
||||
type: FileChangeType.ADDED
|
||||
}]);
|
||||
});
|
||||
|
||||
test('backup file update change event', done => {
|
||||
const expected = joinPath(userDataResource, BACKUPS, 'settings.json');
|
||||
const target = joinPath(localBackupsResource, 'settings.json');
|
||||
testObject.onFileChanges(e => {
|
||||
if (e.contains(expected, FileChangeType.UPDATED)) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
fileEventEmitter.fire([{
|
||||
resource: target,
|
||||
type: FileChangeType.UPDATED
|
||||
}]);
|
||||
});
|
||||
|
||||
test('backup file delete change event', done => {
|
||||
const expected = joinPath(userDataResource, BACKUPS, 'settings.json');
|
||||
const target = joinPath(localBackupsResource, 'settings.json');
|
||||
testObject.onFileChanges(e => {
|
||||
if (e.contains(expected, FileChangeType.DELETED)) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
fileEventEmitter.fire([{
|
||||
resource: target,
|
||||
type: FileChangeType.DELETED
|
||||
}]);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user