From b6c042d0a35b977fc1ab51827805b73dfceefabb Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Thu, 7 May 2020 08:29:24 +0100 Subject: [PATCH] fix(compiler-cli): ensure `MockFileSystem` handles case-sensitivity (#36968) Previously this class used the file passed in directly to look up files in the in-memory mock file-system. But this doesn't match the behaviour of case-insensitive file-systems. Now the look up is done on the canonical file paths. PR Close #36968 --- .../testing/src/mock_file_system.ts | 46 +++++++++++-------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/packages/compiler-cli/src/ngtsc/file_system/testing/src/mock_file_system.ts b/packages/compiler-cli/src/ngtsc/file_system/testing/src/mock_file_system.ts index 7731f5fea6..8382d456f5 100644 --- a/packages/compiler-cli/src/ngtsc/file_system/testing/src/mock_file_system.ts +++ b/packages/compiler-cli/src/ngtsc/file_system/testing/src/mock_file_system.ts @@ -119,7 +119,7 @@ export abstract class MockFileSystem implements FileSystem { } ensureDir(path: AbsoluteFsPath): void { - const segments = this.splitPath(path); + const segments = this.splitPath(path).map(segment => this.getCanonicalPath(segment)); let current: Folder = this._fileTree; // Convert the root folder to a canonical empty string `''` (on Windows it would be `'C:'`). @@ -212,12 +212,29 @@ export abstract class MockFileSystem implements FileSystem { protected abstract splitPath(path: T): string[]; dump(): Folder { - return cloneFolder(this._fileTree); + return this.cloneFolder(this._fileTree); } init(folder: Folder): void { - this._fileTree = cloneFolder(folder); + this._fileTree = this.cloneFolder(folder); } + private cloneFolder(folder: Folder): Folder { + const clone: Folder = {}; + for (const path in folder) { + const item = folder[path]; + const canonicalPath = this.getCanonicalPath(path); + if (isSymLink(item)) { + clone[canonicalPath] = new SymLink(this.getCanonicalPath(item.path)); + } else if (isFolder(item)) { + clone[canonicalPath] = this.cloneFolder(item); + } else { + clone[canonicalPath] = folder[path]; + } + } + return clone; + } + + protected findFromPath(path: AbsoluteFsPath, options?: {followSymLinks: boolean}): FindResult { const followSymLinks = !!options && options.followSymLinks; const segments = this.splitPath(path); @@ -229,7 +246,7 @@ export abstract class MockFileSystem implements FileSystem { segments[0] = ''; let current: Entity|null = this._fileTree; while (segments.length) { - current = current[segments.shift()!]; + current = current[this.getCanonicalPath(segments.shift()!)]; if (current === undefined) { return {path, entity: null}; } @@ -252,10 +269,14 @@ export abstract class MockFileSystem implements FileSystem { } protected splitIntoFolderAndFile(path: AbsoluteFsPath): [AbsoluteFsPath, string] { - const segments = this.splitPath(path); + const segments = this.splitPath(this.getCanonicalPath(path)); const file = segments.pop()!; return [path.substring(0, path.length - file.length - 1) as AbsoluteFsPath, file]; } + + protected getCanonicalPath(p: T): T { + return this.isCaseSensitive() ? p : p.toLowerCase() as T; + } } export interface FindResult { path: AbsoluteFsPath; @@ -300,18 +321,3 @@ export function isSymLink(item: Entity|null): item is SymLink { export function isFolder(item: Entity|null): item is Folder { return item !== null && !isFile(item) && !isSymLink(item); } - -function cloneFolder(folder: Folder): Folder { - const clone: Folder = {}; - for (const path in folder) { - const item = folder[path]; - if (isSymLink(item)) { - clone[path] = new SymLink(item.path); - } else if (isFolder(item)) { - clone[path] = cloneFolder(item); - } else { - clone[path] = folder[path]; - } - } - return clone; -}