提交 44ed24d5 编写于 作者: B Benjamin Pasero

files2 - implement resolving

上级 cd10c5bd
...@@ -701,7 +701,16 @@ export interface IUpdateContentOptions { ...@@ -701,7 +701,16 @@ export interface IUpdateContentOptions {
} }
export interface IResolveFileOptions { export interface IResolveFileOptions {
/**
* Automatically continue resolving children of a directory until the provided resources
* are found.
*/
resolveTo?: URI[]; resolveTo?: URI[];
/**
* Automatically continue resolving children of a directory if the number of children is 1.
*/
resolveSingleChildDescendants?: boolean; resolveSingleChildDescendants?: boolean;
} }
...@@ -1034,10 +1043,6 @@ export interface ILegacyFileService { ...@@ -1034,10 +1043,6 @@ export interface ILegacyFileService {
onFileChanges: Event<FileChangesEvent>; onFileChanges: Event<FileChangesEvent>;
onAfterOperation: Event<FileOperationEvent>; onAfterOperation: Event<FileOperationEvent>;
resolveFile(resource: URI, options?: IResolveFileOptions): Promise<IFileStat>;
resolveFiles(toResolve: { resource: URI, options?: IResolveFileOptions }[]): Promise<IResolveFileResult[]>;
resolveContent(resource: URI, options?: IResolveContentOptions): Promise<IContent>; resolveContent(resource: URI, options?: IResolveContentOptions): Promise<IContent>;
resolveStreamContent(resource: URI, options?: IResolveContentOptions): Promise<IStreamContent>; resolveStreamContent(resource: URI, options?: IResolveContentOptions): Promise<IStreamContent>;
......
...@@ -217,15 +217,6 @@ export class FileService extends Disposable implements ILegacyFileService { ...@@ -217,15 +217,6 @@ export class FileService extends Disposable implements ILegacyFileService {
return resource.scheme === Schemas.file; return resource.scheme === Schemas.file;
} }
resolveFile(resource: uri, options?: IResolveFileOptions): Promise<IFileStat> {
return this.resolve(resource, options);
}
resolveFiles(toResolve: { resource: uri, options?: IResolveFileOptions }[]): Promise<IResolveFileResult[]> {
return Promise.all(toResolve.map(resourceAndOptions => this.resolve(resourceAndOptions.resource, resourceAndOptions.options)
.then(stat => ({ stat, success: true }), error => ({ stat: undefined, success: false }))));
}
resolveContent(resource: uri, options?: IResolveContentOptions): Promise<IContent> { resolveContent(resource: uri, options?: IResolveContentOptions): Promise<IContent> {
return this.resolveStreamContent(resource, options).then(streamContent => { return this.resolveStreamContent(resource, options).then(streamContent => {
return new Promise<IContent>((resolve, reject) => { return new Promise<IContent>((resolve, reject) => {
...@@ -1092,6 +1083,15 @@ export class FileService extends Disposable implements ILegacyFileService { ...@@ -1092,6 +1083,15 @@ export class FileService extends Disposable implements ILegacyFileService {
// Tests only // Tests only
resolveFile(resource: uri, options?: IResolveFileOptions): Promise<IFileStat> {
return this.resolve(resource, options);
}
resolveFiles(toResolve: { resource: uri, options?: IResolveFileOptions }[]): Promise<IResolveFileResult[]> {
return Promise.all(toResolve.map(resourceAndOptions => this.resolve(resourceAndOptions.resource, resourceAndOptions.options)
.then(stat => ({ stat, success: true }), error => ({ stat: undefined, success: false }))));
}
createFolder(resource: uri): Promise<IFileStat> { createFolder(resource: uri): Promise<IFileStat> {
// 1.) Create folder // 1.) Create folder
......
...@@ -13,7 +13,6 @@ import { URI as uri } from 'vs/base/common/uri'; ...@@ -13,7 +13,6 @@ import { URI as uri } from 'vs/base/common/uri';
import * as uuid from 'vs/base/common/uuid'; import * as uuid from 'vs/base/common/uuid';
import * as pfs from 'vs/base/node/pfs'; import * as pfs from 'vs/base/node/pfs';
import * as encodingLib from 'vs/base/node/encoding'; import * as encodingLib from 'vs/base/node/encoding';
import * as utils from 'vs/workbench/services/files/test/electron-browser/utils';
import { TestEnvironmentService, TestContextService, TestTextResourceConfigurationService, TestLifecycleService, TestStorageService } from 'vs/workbench/test/workbenchTestServices'; import { TestEnvironmentService, TestContextService, TestTextResourceConfigurationService, TestLifecycleService, TestStorageService } from 'vs/workbench/test/workbenchTestServices';
import { getRandomTestPath } from 'vs/base/test/node/testUtils'; import { getRandomTestPath } from 'vs/base/test/node/testUtils';
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService'; import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
...@@ -456,33 +455,6 @@ suite('FileService', () => { ...@@ -456,33 +455,6 @@ suite('FileService', () => {
}); });
}); });
test('resolveFile', () => {
return service.resolveFile(uri.file(testDir), { resolveTo: [uri.file(path.join(testDir, 'deep'))] }).then(r => {
assert.equal(r.children!.length, 8);
const deep = utils.getByName(r, 'deep')!;
assert.equal(deep.children!.length, 4);
});
});
test('resolveFiles', () => {
return service.resolveFiles([
{ resource: uri.file(testDir), options: { resolveTo: [uri.file(path.join(testDir, 'deep'))] } },
{ resource: uri.file(path.join(testDir, 'deep')) }
]).then(res => {
const r1 = res[0].stat!;
assert.equal(r1.children!.length, 8);
const deep = utils.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('updateContent', () => { test('updateContent', () => {
const resource = uri.file(path.join(testDir, 'small.txt')); const resource = uri.file(path.join(testDir, 'small.txt'));
......
...@@ -4,13 +4,17 @@ ...@@ -4,13 +4,17 @@
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { Disposable, IDisposable, toDisposable, combinedDisposable } from 'vs/base/common/lifecycle'; import { Disposable, IDisposable, toDisposable, combinedDisposable } from 'vs/base/common/lifecycle';
import { IFileService, IResolveFileOptions, IResourceEncodings, FileChangesEvent, FileOperationEvent, IFileSystemProviderRegistrationEvent, IFileSystemProvider, IFileStat, IResolveFileResult, IResolveContentOptions, IContent, IStreamContent, ITextSnapshot, IUpdateContentOptions, ICreateFileOptions, IFileSystemProviderActivationEvent, FileOperationError, FileOperationResult, FileOperation, FileSystemProviderCapabilities, FileType, toFileSystemProviderErrorCode, FileSystemProviderErrorCode } from 'vs/platform/files/common/files'; import { IFileService, IResolveFileOptions, IResourceEncodings, FileChangesEvent, FileOperationEvent, IFileSystemProviderRegistrationEvent, IFileSystemProvider, IFileStat, IResolveFileResult, IResolveContentOptions, IContent, IStreamContent, ITextSnapshot, IUpdateContentOptions, ICreateFileOptions, IFileSystemProviderActivationEvent, FileOperationError, FileOperationResult, FileOperation, FileSystemProviderCapabilities, FileType, toFileSystemProviderErrorCode, FileSystemProviderErrorCode, IStat } from 'vs/platform/files/common/files';
import { URI } from 'vs/base/common/uri'; import { URI } from 'vs/base/common/uri';
import { Event, Emitter } from 'vs/base/common/event'; import { Event, Emitter } from 'vs/base/common/event';
import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { isAbsolutePath, dirname, basename, joinPath, isEqual } from 'vs/base/common/resources'; import { isAbsolutePath, dirname, basename, joinPath, isEqual } from 'vs/base/common/resources';
import { localize } from 'vs/nls'; import { localize } from 'vs/nls';
import { TernarySearchTree } from 'vs/base/common/map';
import { isNonEmptyArray } from 'vs/base/common/arrays';
import { getBaseLabel } from 'vs/base/common/labels';
import { ILogService } from 'vs/platform/log/common/log';
export class FileService2 extends Disposable implements IFileService { export class FileService2 extends Disposable implements IFileService {
...@@ -29,6 +33,10 @@ export class FileService2 extends Disposable implements IFileService { ...@@ -29,6 +33,10 @@ export class FileService2 extends Disposable implements IFileService {
_serviceBrand: ServiceIdentifier<any>; _serviceBrand: ServiceIdentifier<any>;
constructor(@ILogService private logService: ILogService) {
super();
}
//#region File System Provider //#region File System Provider
private _onDidChangeFileSystemProviderRegistrations: Emitter<IFileSystemProviderRegistrationEvent> = this._register(new Emitter<IFileSystemProviderRegistrationEvent>()); private _onDidChangeFileSystemProviderRegistrations: Emitter<IFileSystemProviderRegistrationEvent> = this._register(new Emitter<IFileSystemProviderRegistrationEvent>());
...@@ -69,7 +77,7 @@ export class FileService2 extends Disposable implements IFileService { ...@@ -69,7 +77,7 @@ export class FileService2 extends Disposable implements IFileService {
]); ]);
} }
activateProvider(scheme: string): Promise<void> { async activateProvider(scheme: string): Promise<void> {
// Emit an event that we are about to activate a provider with the given scheme. // Emit an event that we are about to activate a provider with the given scheme.
// Listeners can participate in the activation by registering a provider for it. // Listeners can participate in the activation by registering a provider for it.
...@@ -89,7 +97,7 @@ export class FileService2 extends Disposable implements IFileService { ...@@ -89,7 +97,7 @@ export class FileService2 extends Disposable implements IFileService {
// If the provider is not yet there, make sure to join on the listeners assuming // If the provider is not yet there, make sure to join on the listeners assuming
// that it takes a bit longer to register the file system provider. // that it takes a bit longer to register the file system provider.
return Promise.all(joiners).then(() => undefined); await Promise.all(joiners);
} }
canHandleResource(resource: URI): boolean { canHandleResource(resource: URI): boolean {
...@@ -129,16 +137,134 @@ export class FileService2 extends Disposable implements IFileService { ...@@ -129,16 +137,134 @@ export class FileService2 extends Disposable implements IFileService {
//#region File Metadata Resolving //#region File Metadata Resolving
resolveFile(resource: URI, options?: IResolveFileOptions): Promise<IFileStat> { async resolveFile(resource: URI, options?: IResolveFileOptions): Promise<IFileStat> {
return this._impl.resolveFile(resource, options); try {
return await this.doResolveFile(resource, options);
} catch (error) {
// Specially handle file not found case as file operation result
if (toFileSystemProviderErrorCode(error) === FileSystemProviderErrorCode.FileNotFound) {
throw new FileOperationError(
localize('fileNotFoundError', "File not found ({0})", resource.toString(true)),
FileOperationResult.FILE_NOT_FOUND
);
}
// Bubble up any other error as is
throw error;
}
}
private async doResolveFile(resource: URI, options?: IResolveFileOptions): Promise<IFileStat> {
const provider = await this.withProvider(resource);
// leverage a trie to check for recursive resolving
const to = options && options.resolveTo;
const trie = TernarySearchTree.forPaths<true>();
trie.set(resource.toString(), true);
if (isNonEmptyArray(to)) {
to.forEach(uri => trie.set(uri.toString(), true));
}
const stat = await provider.stat(resource);
return await this.toFileStat(provider, resource, stat, async stat => {
// check for recursive resolving
if (Boolean(trie.findSuperstr(stat.resource.toString()) || trie.get(stat.resource.toString()))) {
return true;
}
// check for resolving single child folders
if (stat.isDirectory && options && options.resolveSingleChildDescendants) {
try {
return (await provider.readdir(resource)).length === 1;
} catch (error) {
return false;
}
}
return false;
});
}
private async toFileStat(provider: IFileSystemProvider, resource: URI, stat: IStat, recurse: (stat: IFileStat) => Promise<boolean>): Promise<IFileStat> {
// convert to file stat
const fileStat: IFileStat = {
resource,
name: getBaseLabel(resource),
isDirectory: (stat.type & FileType.Directory) !== 0,
isSymbolicLink: (stat.type & FileType.SymbolicLink) !== 0,
isReadonly: !!(provider.capabilities & FileSystemProviderCapabilities.Readonly),
mtime: stat.mtime,
size: stat.size,
etag: stat.mtime.toString(29) + stat.size.toString(31),
};
// check to recurse for directories
if (fileStat.isDirectory && await recurse(fileStat)) {
try {
const entries = await provider.readdir(resource);
fileStat.children = await Promise.all(entries.map(async entry => {
const childResource = joinPath(resource, entry[0]);
const childStat = await provider.stat(childResource);
return this.toFileStat(provider, childResource, childStat, recurse);
}));
} catch (error) {
this.logService.trace(error);
fileStat.children = []; // gracefully handle errors, we may not have permissions to read
} }
resolveFiles(toResolve: { resource: URI; options?: IResolveFileOptions; }[]): Promise<IResolveFileResult[]> { return fileStat;
return this._impl.resolveFiles(toResolve);
} }
existsFile(resource: URI): Promise<boolean> { return Promise.resolve(fileStat);
return this.resolveFile(resource).then(_ => true, error => false); }
async resolveFiles(toResolve: { resource: URI; options?: IResolveFileOptions; }[]): Promise<IResolveFileResult[]> {
// soft-groupBy, keep order, don't rearrange/merge groups
const groups: Array<typeof toResolve> = [];
let group: typeof toResolve | undefined;
for (const request of toResolve) {
if (!group || group[0].resource.scheme !== request.resource.scheme) {
group = [];
groups.push(group);
}
group.push(request);
}
// resolve files
const result: IResolveFileResult[] = [];
for (const group of groups) {
for (const groupEntry of group) {
try {
const stat = await this.doResolveFile(groupEntry.resource, groupEntry.options);
result.push({ stat, success: true });
} catch (error) {
this.logService.trace(error);
result.push({ stat: undefined, success: false });
}
}
}
return result;
}
async existsFile(resource: URI): Promise<boolean> {
try {
await this.resolveFile(resource);
return true;
} catch (error) {
return false;
}
} }
//#endregion //#endregion
...@@ -200,11 +326,11 @@ export class FileService2 extends Disposable implements IFileService { ...@@ -200,11 +326,11 @@ export class FileService2 extends Disposable implements IFileService {
} }
break; // we have hit a directory that exists -> good break; // we have hit a directory that exists -> good
} catch (e) { } catch (error) {
// Bubble up any other error that is not file not found // Bubble up any other error that is not file not found
if (toFileSystemProviderErrorCode(e) !== FileSystemProviderErrorCode.FileNotFound) { if (toFileSystemProviderErrorCode(error) !== FileSystemProviderErrorCode.FileNotFound) {
throw e; throw error;
} }
// Upon error, remember directories that need to be created // Upon error, remember directories that need to be created
......
...@@ -10,8 +10,9 @@ import { IFileSystemProvider, FileSystemProviderCapabilities, IFileChange, IWatc ...@@ -10,8 +10,9 @@ import { IFileSystemProvider, FileSystemProviderCapabilities, IFileChange, IWatc
import { URI } from 'vs/base/common/uri'; import { URI } from 'vs/base/common/uri';
import { Event, Emitter } from 'vs/base/common/event'; import { Event, Emitter } from 'vs/base/common/event';
import { isLinux } from 'vs/base/common/platform'; import { isLinux } from 'vs/base/common/platform';
import { statLink } from 'vs/base/node/pfs'; import { statLink, readdir } from 'vs/base/node/pfs';
import { normalize } from 'vs/base/common/path'; import { normalize } from 'vs/base/common/path';
import { joinPath } from 'vs/base/common/resources';
export class DiskFileSystemProvider extends Disposable implements IFileSystemProvider { export class DiskFileSystemProvider extends Disposable implements IFileSystemProvider {
...@@ -54,8 +55,22 @@ export class DiskFileSystemProvider extends Disposable implements IFileSystemPro ...@@ -54,8 +55,22 @@ export class DiskFileSystemProvider extends Disposable implements IFileSystemPro
} }
} }
readdir(resource: URI): Promise<[string, FileType][]> { async readdir(resource: URI): Promise<[string, FileType][]> {
throw new Error('Method not implemented.'); try {
const children = await readdir(this.toFilePath(resource));
const result: [string, FileType][] = [];
for (let i = 0; i < children.length; i++) {
const child = children[i];
const stat = await this.stat(joinPath(resource, child));
result.push([child, stat.type]);
}
return result;
} catch (error) {
throw this.toFileSystemProviderError(error);
}
} }
//#endregion //#endregion
......
...@@ -10,17 +10,33 @@ import { Schemas } from 'vs/base/common/network'; ...@@ -10,17 +10,33 @@ import { Schemas } from 'vs/base/common/network';
import { DiskFileSystemProvider } from 'vs/workbench/services/files2/node/diskFileSystemProvider'; import { DiskFileSystemProvider } from 'vs/workbench/services/files2/node/diskFileSystemProvider';
import { getRandomTestPath } from 'vs/base/test/node/testUtils'; import { getRandomTestPath } from 'vs/base/test/node/testUtils';
import { generateUuid } from 'vs/base/common/uuid'; import { generateUuid } from 'vs/base/common/uuid';
import { join } from 'vs/base/common/path'; import { join, basename } from 'vs/base/common/path';
import { getPathFromAmdModule } from 'vs/base/common/amd'; import { getPathFromAmdModule } from 'vs/base/common/amd';
import { copy, del } from 'vs/base/node/pfs'; import { copy, del } from 'vs/base/node/pfs';
import { URI } from 'vs/base/common/uri'; import { URI } from 'vs/base/common/uri';
import { existsSync } from 'fs'; import { existsSync } from 'fs';
import { FileOperation, FileOperationEvent } from 'vs/platform/files/common/files'; import { FileOperation, FileOperationEvent, IFileStat } from 'vs/platform/files/common/files';
import { FileService } from 'vs/workbench/services/files/node/fileService'; import { FileService } from 'vs/workbench/services/files/node/fileService';
import { TestContextService, TestEnvironmentService, TestTextResourceConfigurationService, TestLifecycleService, TestStorageService } from 'vs/workbench/test/workbenchTestServices'; import { TestContextService, TestEnvironmentService, TestTextResourceConfigurationService, TestLifecycleService, TestStorageService } from 'vs/workbench/test/workbenchTestServices';
import { Workspace, toWorkspaceFolders } from 'vs/platform/workspace/common/workspace'; import { Workspace, toWorkspaceFolders } from 'vs/platform/workspace/common/workspace';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService'; import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
import { NullLogService } from 'vs/platform/log/common/log';
import { isLinux } from 'vs/base/common/platform';
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;
}
suite('Disk File Service', () => { suite('Disk File Service', () => {
...@@ -30,7 +46,7 @@ suite('Disk File Service', () => { ...@@ -30,7 +46,7 @@ suite('Disk File Service', () => {
let testDir: string; let testDir: string;
setup(async () => { setup(async () => {
service = new FileService2(); service = new FileService2(new NullLogService());
service.registerProvider(Schemas.file, new DiskFileSystemProvider()); service.registerProvider(Schemas.file, new DiskFileSystemProvider());
const id = generateUuid(); const id = generateUuid();
...@@ -49,60 +65,217 @@ suite('Disk File Service', () => { ...@@ -49,60 +65,217 @@ suite('Disk File Service', () => {
}); });
test('createFolder', async () => { test('createFolder', async () => {
let event: FileOperationEvent; let event: FileOperationEvent | undefined;
const toDispose = service.onAfterOperation(e => { const toDispose = service.onAfterOperation(e => {
event = e; event = e;
}); });
return service.resolveFile(URI.file(testDir)).then(parent => { const parent = await service.resolveFile(URI.file(testDir));
const resource = URI.file(join(parent.resource.fsPath, 'newFolder')); const resource = URI.file(join(parent.resource.fsPath, 'newFolder'));
return service.createFolder(resource).then(f => { const folder = await service.createFolder(resource);
assert.equal(f.name, 'newFolder');
assert.equal(existsSync(f.resource.fsPath), true); assert.equal(folder.name, 'newFolder');
assert.equal(existsSync(folder.resource.fsPath), true);
assert.ok(event); assert.ok(event);
assert.equal(event.resource.fsPath, resource.fsPath); assert.equal(event!.resource.fsPath, resource.fsPath);
assert.equal(event.operation, FileOperation.CREATE); assert.equal(event!.operation, FileOperation.CREATE);
assert.equal(event.target!.resource.fsPath, resource.fsPath); assert.equal(event!.target!.resource.fsPath, resource.fsPath);
assert.equal(event.target!.isDirectory, true); assert.equal(event!.target!.isDirectory, true);
toDispose.dispose(); toDispose.dispose();
}); });
});
});
test('createFolder: creating multiple folders at once', function () { test('createFolder: creating multiple folders at once', async function () {
let event: FileOperationEvent; let event: FileOperationEvent;
const toDispose = service.onAfterOperation(e => { const toDispose = service.onAfterOperation(e => {
event = e; event = e;
}); });
const multiFolderPaths = ['a', 'couple', 'of', 'folders']; const multiFolderPaths = ['a', 'couple', 'of', 'folders'];
return service.resolveFile(URI.file(testDir)).then(parent => { const parent = await service.resolveFile(URI.file(testDir));
const resource = URI.file(join(parent.resource.fsPath, ...multiFolderPaths)); const resource = URI.file(join(parent.resource.fsPath, ...multiFolderPaths));
return service.createFolder(resource).then(f => { const folder = await service.createFolder(resource);
const lastFolderName = multiFolderPaths[multiFolderPaths.length - 1]; const lastFolderName = multiFolderPaths[multiFolderPaths.length - 1];
assert.equal(f.name, lastFolderName); assert.equal(folder.name, lastFolderName);
assert.equal(existsSync(f.resource.fsPath), true); assert.equal(existsSync(folder.resource.fsPath), true);
assert.ok(event!);
assert.equal(event!.resource.fsPath, resource.fsPath);
assert.equal(event!.operation, FileOperation.CREATE);
assert.equal(event!.target!.resource.fsPath, resource.fsPath);
assert.equal(event!.target!.isDirectory, true);
assert.ok(event);
assert.equal(event.resource.fsPath, resource.fsPath);
assert.equal(event.operation, FileOperation.CREATE);
assert.equal(event.target!.resource.fsPath, resource.fsPath);
assert.equal(event.target!.isDirectory, true);
toDispose.dispose(); toDispose.dispose();
}); });
});
});
test('existsFile', () => { test('existsFile', async () => {
return service.existsFile(URI.file(testDir)).then((exists) => { let exists = await service.existsFile(URI.file(testDir));
assert.equal(exists, true); assert.equal(exists, true);
return service.existsFile(URI.file(testDir + 'something')).then((exists) => { exists = await service.existsFile(URI.file(testDir + 'something'));
assert.equal(exists, false); assert.equal(exists, false);
}); });
test('resolveFile', async () => {
const resolved = await service.resolveFile(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('resolveFile - directory', async () => {
const testsElements = ['examples', 'other', 'index.html', 'site.css'];
const result = await service.resolveFile(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('resolveFile - directory - resolveTo single directory', async () => {
const resolverFixturesPath = getPathFromAmdModule(require, './fixtures/resolver');
const result = await service.resolveFile(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 single directory - mixed casing', async () => {
const resolverFixturesPath = getPathFromAmdModule(require, './fixtures/resolver');
const result = await service.resolveFile(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');
if (isLinux) { // Linux has case sensitive file system
assert.ok(deep);
assert.ok(!deep!.children); // not resolved because we got instructed to resolve other/Deep with capital D
} else {
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.resolveFile(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.resolveFile(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('resolveFiles', async () => {
const res = await service.resolveFiles([
{ 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');
}); });
}); });
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册