From c7091ab01b70fd3369979fb7bfe811e6dd07915f Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Tue, 29 May 2018 13:45:20 -0700 Subject: [PATCH] Add test that verifies watched directories and files when resolution of module references sibling folder to root with symlLink --- src/harness/unittests/tscWatchMode.ts | 65 ++++++++++++++++--- .../unittests/tsserverProjectSystem.ts | 23 +++---- src/harness/virtualFileSystemWithWatch.ts | 32 ++++++--- 3 files changed, 88 insertions(+), 32 deletions(-) diff --git a/src/harness/unittests/tscWatchMode.ts b/src/harness/unittests/tscWatchMode.ts index 44b6c8ea887..bd157ac5763 100644 --- a/src/harness/unittests/tscWatchMode.ts +++ b/src/harness/unittests/tscWatchMode.ts @@ -10,9 +10,12 @@ namespace ts.tscWatch { import checkArray = TestFSWithWatch.checkArray; import libFile = TestFSWithWatch.libFile; import checkWatchedFiles = TestFSWithWatch.checkWatchedFiles; + import checkWatchedFilesDetailed = TestFSWithWatch.checkWatchedFilesDetailed; import checkWatchedDirectories = TestFSWithWatch.checkWatchedDirectories; + import checkWatchedDirectoriesDetailed = TestFSWithWatch.checkWatchedDirectoriesDetailed; import checkOutputContains = TestFSWithWatch.checkOutputContains; import checkOutputDoesNotContain = TestFSWithWatch.checkOutputDoesNotContain; + import Tsc_WatchDirectory = TestFSWithWatch.Tsc_WatchDirectory; export function checkProgramActualFiles(program: Program, expectedFiles: string[]) { checkArray(`Program actual files`, program.getSourceFiles().map(file => file.fileName), expectedFiles); @@ -2379,7 +2382,7 @@ declare module "fs" { }); describe("tsc-watch when watchDirectories implementation", () => { - function verifyRenamingFileInSubFolder(tscWatchDirectory: TestFSWithWatch.Tsc_WatchDirectory) { + function verifyRenamingFileInSubFolder(tscWatchDirectory: Tsc_WatchDirectory) { const projectFolder = "/a/username/project"; const projectSrcFolder = `${projectFolder}/src`; const configFile: File = { @@ -2399,8 +2402,8 @@ declare module "fs" { const projectFolders = [projectFolder, projectSrcFolder, `${projectFolder}/node_modules/@types`]; // Watching files config file, file, lib file const expectedWatchedFiles = files.map(f => f.path); - const expectedWatchedDirectories = tscWatchDirectory === TestFSWithWatch.Tsc_WatchDirectory.NonRecursiveWatchDirectory ? projectFolders : emptyArray; - if (tscWatchDirectory === TestFSWithWatch.Tsc_WatchDirectory.WatchFile) { + const expectedWatchedDirectories = tscWatchDirectory === Tsc_WatchDirectory.NonRecursiveWatchDirectory ? projectFolders : emptyArray; + if (tscWatchDirectory === Tsc_WatchDirectory.WatchFile) { expectedWatchedFiles.push(...projectFolders); } @@ -2410,7 +2413,7 @@ declare module "fs" { file.path = file.path.replace("file1.ts", "file2.ts"); expectedWatchedFiles[0] = file.path; host.reloadFS(files); - if (tscWatchDirectory === TestFSWithWatch.Tsc_WatchDirectory.DynamicPolling) { + if (tscWatchDirectory === Tsc_WatchDirectory.DynamicPolling) { // With dynamic polling the fs change would be detected only by running timeouts host.runQueuedTimeoutCallbacks(); } @@ -2429,21 +2432,21 @@ declare module "fs" { checkWatchedDirectories(host, emptyArray, /*recursive*/ true); // Watching config file, file, lib file and directories - TestFSWithWatch.checkMultiMapEachKeyWithCount("watchedFiles", host.watchedFiles, expectedWatchedFiles, 1); - TestFSWithWatch.checkMultiMapEachKeyWithCount("watchedDirectories", host.watchedDirectories, expectedWatchedDirectories, 1); + checkWatchedFilesDetailed(host, expectedWatchedFiles, 1); + checkWatchedDirectoriesDetailed(host, expectedWatchedDirectories, 1, /*recursive*/ false); } } it("uses watchFile when renaming file in subfolder", () => { - verifyRenamingFileInSubFolder(TestFSWithWatch.Tsc_WatchDirectory.WatchFile); + verifyRenamingFileInSubFolder(Tsc_WatchDirectory.WatchFile); }); it("uses non recursive watchDirectory when renaming file in subfolder", () => { - verifyRenamingFileInSubFolder(TestFSWithWatch.Tsc_WatchDirectory.NonRecursiveWatchDirectory); + verifyRenamingFileInSubFolder(Tsc_WatchDirectory.NonRecursiveWatchDirectory); }); it("uses non recursive dynamic polling when renaming file in subfolder", () => { - verifyRenamingFileInSubFolder(TestFSWithWatch.Tsc_WatchDirectory.DynamicPolling); + verifyRenamingFileInSubFolder(Tsc_WatchDirectory.DynamicPolling); }); it("when there are symlinks to folders in recursive folders", () => { @@ -2482,7 +2485,7 @@ declare module "fs" { }; const files = [file1, tsconfig, realA, realB, symLinkA, symLinkB, symLinkBInA, symLinkAInB]; const environmentVariables = createMap(); - environmentVariables.set("TSC_WATCHDIRECTORY", TestFSWithWatch.Tsc_WatchDirectory.NonRecursiveWatchDirectory); + environmentVariables.set("TSC_WATCHDIRECTORY", Tsc_WatchDirectory.NonRecursiveWatchDirectory); const host = createWatchedSystem(files, { environmentVariables, currentDirectory: cwd }); createWatchOfConfigFile("tsconfig.json", host); checkWatchedDirectories(host, emptyArray, /*recursive*/ true); @@ -2491,4 +2494,46 @@ declare module "fs" { }); }); }); + + describe("tsc-watch with modules linked to sibling folder", () => { + const projectRoot = "/user/username/projects/project"; + const mainPackageRoot = `${projectRoot}/main`; + const linkedPackageRoot = `${projectRoot}/linked-package`; + const mainFile: File = { + path: `${mainPackageRoot}/index.ts`, + content: "import { Foo } from '@scoped/linked-package'" + }; + const config: File = { + path: `${mainPackageRoot}/tsconfig.json`, + content: JSON.stringify({ + compilerOptions: { module: "commonjs", moduleResolution: "node", baseUrl: ".", rootDir: "." }, + files: ["index.ts"] + }) + }; + const linkedPackageInMain: SymLink = { + path: `${mainPackageRoot}/node_modules/@scoped/linked-package`, + symLink: `${linkedPackageRoot}` + }; + const linkedPackageJson: File = { + path: `${linkedPackageRoot}/package.json`, + content: JSON.stringify({ name: "@scoped/linked-package", version: "0.0.1", types: "dist/index.d.ts", main: "dist/index.js" }) + }; + const linkedPackageIndex: File = { + path: `${linkedPackageRoot}/dist/index.d.ts`, + content: "export * from './other';" + }; + const linkedPackageOther: File = { + path: `${linkedPackageRoot}/dist/other.d.ts`, + content: 'export declare const Foo = "BAR";' + }; + + it("verify watched directories", () => { + const files = [libFile, mainFile, config, linkedPackageInMain, linkedPackageJson, linkedPackageIndex, linkedPackageOther]; + const host = createWatchedSystem(files, { currentDirectory: mainPackageRoot }); + createWatchOfConfigFile("tsconfig.json", host); + checkWatchedFilesDetailed(host, [libFile.path, mainFile.path, config.path, linkedPackageIndex.path, linkedPackageOther.path], 1); + checkWatchedDirectories(host, emptyArray, /*recursive*/ false); + checkWatchedDirectoriesDetailed(host, [mainPackageRoot, projectRoot, `${mainPackageRoot}/node_modules/@types`, `${projectRoot}/node_modules/@types`], 1, /*recursive*/ true); + }); + }); } diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index f6efe8b99dd..88fe374c398 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -19,6 +19,7 @@ namespace ts.projectSystem { export import checkWatchedDirectories = TestFSWithWatch.checkWatchedDirectories; export import checkWatchedDirectoriesDetailed = TestFSWithWatch.checkWatchedDirectoriesDetailed; import safeList = TestFSWithWatch.safeList; + import Tsc_WatchDirectory = TestFSWithWatch.Tsc_WatchDirectory; export const customTypesMap = { path: "/typesMap.json", @@ -6259,7 +6260,7 @@ namespace ts.projectSystem { } function verifyCalledOnEachEntryNTimes(callback: CalledMaps, expectedKeys: ReadonlyArray, nTimes: number) { - TestFSWithWatch.checkMultiMapEachKeyWithCount(callback, calledMaps[callback], expectedKeys, nTimes); + TestFSWithWatch.checkMultiMapKeyCount(callback, calledMaps[callback], expectedKeys, nTimes); } function verifyNoHostCalls() { @@ -7813,14 +7814,10 @@ new C();` checkCompleteEvent(session, 2, expectedSequenceId); } - function createSingleWatchMap(paths: string[]) { - return arrayToMap(paths, p => p, () => 1); - } - function verifyWatchedFilesAndDirectories(host: TestServerHost, files: string[], directories: string[]) { - checkWatchedFilesDetailed(host, createSingleWatchMap(files.filter(f => f !== recognizersDateTimeSrcFile.path))); + checkWatchedFilesDetailed(host, files.filter(f => f !== recognizersDateTimeSrcFile.path), 1); checkWatchedDirectories(host, emptyArray, /*recursive*/ false); - checkWatchedDirectoriesDetailed(host, createSingleWatchMap(directories), /*recursive*/ true); + checkWatchedDirectoriesDetailed(host, directories, 1, /*recursive*/ true); } function createSessionAndOpenFile(host: TestServerHost) { @@ -8315,7 +8312,7 @@ new C();` }); describe("tsserverProjectSystem watchDirectories implementation", () => { - function verifyCompletionListWithNewFileInSubFolder(tscWatchDirectory: TestFSWithWatch.Tsc_WatchDirectory) { + function verifyCompletionListWithNewFileInSubFolder(tscWatchDirectory: Tsc_WatchDirectory) { const projectFolder = "/a/username/project"; const projectSrcFolder = `${projectFolder}/src`; const configFile: File = { @@ -8336,9 +8333,9 @@ new C();` // All closed files(files other than index), project folder, project/src folder and project/node_modules/@types folder const expectedWatchedFiles = arrayToMap(fileNames.slice(1), s => s, () => 1); const expectedWatchedDirectories = createMap(); - const mapOfDirectories = tscWatchDirectory === TestFSWithWatch.Tsc_WatchDirectory.NonRecursiveWatchDirectory ? + const mapOfDirectories = tscWatchDirectory === Tsc_WatchDirectory.NonRecursiveWatchDirectory ? expectedWatchedDirectories : - tscWatchDirectory === TestFSWithWatch.Tsc_WatchDirectory.WatchFile ? + tscWatchDirectory === Tsc_WatchDirectory.WatchFile ? expectedWatchedFiles : createMap(); // For failed resolution lookup and tsconfig files @@ -8385,15 +8382,15 @@ new C();` } it("uses watchFile when file is added to subfolder, completion list has new file", () => { - verifyCompletionListWithNewFileInSubFolder(TestFSWithWatch.Tsc_WatchDirectory.WatchFile); + verifyCompletionListWithNewFileInSubFolder(Tsc_WatchDirectory.WatchFile); }); it("uses non recursive watchDirectory when file is added to subfolder, completion list has new file", () => { - verifyCompletionListWithNewFileInSubFolder(TestFSWithWatch.Tsc_WatchDirectory.NonRecursiveWatchDirectory); + verifyCompletionListWithNewFileInSubFolder(Tsc_WatchDirectory.NonRecursiveWatchDirectory); }); it("uses dynamic polling when file is added to subfolder, completion list has new file", () => { - verifyCompletionListWithNewFileInSubFolder(TestFSWithWatch.Tsc_WatchDirectory.DynamicPolling); + verifyCompletionListWithNewFileInSubFolder(Tsc_WatchDirectory.DynamicPolling); }); }); diff --git a/src/harness/virtualFileSystemWithWatch.ts b/src/harness/virtualFileSystemWithWatch.ts index 0afa67880a4..484d3302c1b 100644 --- a/src/harness/virtualFileSystemWithWatch.ts +++ b/src/harness/virtualFileSystemWithWatch.ts @@ -175,7 +175,10 @@ interface Array {}` } } - export function checkMultiMapKeyCount(caption: string, actual: MultiMap, expectedKeys: Map) { + export function checkMultiMapKeyCount(caption: string, actual: MultiMap, expectedKeys: ReadonlyMap): void; + export function checkMultiMapKeyCount(caption: string, actual: MultiMap, expectedKeys: ReadonlyArray, eachKeyCount: number): void; + export function checkMultiMapKeyCount(caption: string, actual: MultiMap, expectedKeysMapOrArray: ReadonlyMap | ReadonlyArray, eachKeyCount?: number) { + const expectedKeys = isArray(expectedKeysMapOrArray) ? arrayToMap(expectedKeysMapOrArray, s => s, () => eachKeyCount!) : expectedKeysMapOrArray; verifyMapSize(caption, actual, arrayFrom(expectedKeys.keys())); expectedKeys.forEach((count, name) => { assert.isTrue(actual.has(name), `${caption}: expected to contain ${name}, actual keys: ${arrayFrom(actual.keys())}`); @@ -183,10 +186,6 @@ interface Array {}` }); } - export function checkMultiMapEachKeyWithCount(caption: string, actual: MultiMap, expectedKeys: ReadonlyArray, count: number) { - return checkMultiMapKeyCount(caption, actual, arrayToMap(expectedKeys, s => s, () => count)); - } - export function checkArray(caption: string, actual: ReadonlyArray, expected: ReadonlyArray) { assert.equal(actual.length, expected.length, `${caption}: incorrect actual number of files, expected:\r\n${expected.join("\r\n")}\r\ngot: ${actual.join("\r\n")}`); for (const f of expected) { @@ -198,16 +197,31 @@ interface Array {}` checkMapKeys("watchedFiles", host.watchedFiles, expectedFiles); } - export function checkWatchedFilesDetailed(host: TestServerHost, expectedFiles: Map) { - checkMultiMapKeyCount("watchedFiles", host.watchedFiles, expectedFiles); + export function checkWatchedFilesDetailed(host: TestServerHost, expectedFiles: ReadonlyMap): void; + export function checkWatchedFilesDetailed(host: TestServerHost, expectedFiles: ReadonlyArray, eachFileWatchCount: number): void; + export function checkWatchedFilesDetailed(host: TestServerHost, expectedFiles: ReadonlyMap | ReadonlyArray, eachFileWatchCount?: number) { + if (isArray(expectedFiles)) { + checkMultiMapKeyCount("watchedFiles", host.watchedFiles, expectedFiles, eachFileWatchCount!); + } + else { + checkMultiMapKeyCount("watchedFiles", host.watchedFiles, expectedFiles); + } } export function checkWatchedDirectories(host: TestServerHost, expectedDirectories: string[], recursive: boolean) { checkMapKeys(`watchedDirectories${recursive ? " recursive" : ""}`, recursive ? host.watchedDirectoriesRecursive : host.watchedDirectories, expectedDirectories); } - export function checkWatchedDirectoriesDetailed(host: TestServerHost, expectedDirectories: Map, recursive: boolean) { - checkMultiMapKeyCount(`watchedDirectories${recursive ? " recursive" : ""}`, recursive ? host.watchedDirectoriesRecursive : host.watchedDirectories, expectedDirectories); + export function checkWatchedDirectoriesDetailed(host: TestServerHost, expectedDirectories: ReadonlyMap, recursive: boolean): void; + export function checkWatchedDirectoriesDetailed(host: TestServerHost, expectedDirectories: ReadonlyArray, eachDirectoryWatchCount: number, recursive: boolean): void; + export function checkWatchedDirectoriesDetailed(host: TestServerHost, expectedDirectories: ReadonlyMap | ReadonlyArray, recursiveOrEachDirectoryWatchCount: boolean | number, recursive?: boolean) { + if (isArray(expectedDirectories)) { + checkMultiMapKeyCount(`watchedDirectories${recursive ? " recursive" : ""}`, recursive ? host.watchedDirectoriesRecursive : host.watchedDirectories, expectedDirectories, recursiveOrEachDirectoryWatchCount as number); + } + else { + recursive = recursiveOrEachDirectoryWatchCount as boolean; + checkMultiMapKeyCount(`watchedDirectories${recursive ? " recursive" : ""}`, recursive ? host.watchedDirectoriesRecursive : host.watchedDirectories, expectedDirectories); + } } export function checkOutputContains(host: TestServerHost, expected: ReadonlyArray) {