diff --git a/src/harness/virtualFileSystemWithWatch.ts b/src/harness/virtualFileSystemWithWatch.ts index 796d963f44b..c6cbef9c083 100644 --- a/src/harness/virtualFileSystemWithWatch.ts +++ b/src/harness/virtualFileSystemWithWatch.ts @@ -588,6 +588,98 @@ interface Array { length: number; [n: number]: T; }` } } + export function snap(fshost: VirtualFS.VirtualServerHost): ESMap { + const result = new Map(); + fshost.fs.forEach((value, key) => { + const cloneValue = clone(value); + if (isFsFolder(cloneValue)) { + cloneValue.entries = cloneValue.entries.map(clone) as SortedArray; + } + result.set(key, cloneValue); + }); + + return result; + } + + export function diff(fshost: VirtualFS.VirtualServerHost, baseline: string[], base: ESMap = new Map()) { + fshost.fs.forEach(newFsEntry => { + diffFsEntry(baseline, base.get(newFsEntry.path), newFsEntry, fshost.writtenFiles); + }); + base.forEach(oldFsEntry => { + const newFsEntry = fshost.fs.get(oldFsEntry.path); + if (!newFsEntry) { + diffFsEntry(baseline, oldFsEntry, newFsEntry, fshost.writtenFiles); + } + }); + baseline.push(""); + } + + function diffFsFile(baseline: string[], fsEntry: FsFile) { + baseline.push(`//// [${fsEntry.fullPath}]\r\n${fsEntry.content}`, ""); + } + + function diffFsSymLink(baseline: string[], fsEntry: FsSymLink) { + baseline.push(`//// [${fsEntry.fullPath}] symlink(${fsEntry.symLink})`); + } + + function diffFsEntry(baseline: string[], oldFsEntry: FSEntry | undefined, newFsEntry: FSEntry | undefined, writtenFiles: ESMap | undefined): void { + const file = newFsEntry && newFsEntry.fullPath; + if (isFsFile(oldFsEntry)) { + if (isFsFile(newFsEntry)) { + if (oldFsEntry.content !== newFsEntry.content) { + diffFsFile(baseline, newFsEntry); + } + else if (oldFsEntry.modifiedTime !== newFsEntry.modifiedTime) { + if (oldFsEntry.fullPath !== newFsEntry.fullPath) { + baseline.push(`//// [${file}] file was renamed from file ${oldFsEntry.fullPath}`); + } + else if (writtenFiles && !writtenFiles.has(newFsEntry.path)) { + baseline.push(`//// [${file}] file changed its modified time`); + } + else { + baseline.push(`//// [${file}] file written with same contents`); + } + } + } + else { + baseline.push(`//// [${oldFsEntry.fullPath}] deleted`); + if (isFsSymLink(newFsEntry)) { + diffFsSymLink(baseline, newFsEntry); + } + } + } + else if (isFsSymLink(oldFsEntry)) { + if (isFsSymLink(newFsEntry)) { + if (oldFsEntry.symLink !== newFsEntry.symLink) { + diffFsSymLink(baseline, newFsEntry); + } + else if (oldFsEntry.modifiedTime !== newFsEntry.modifiedTime) { + if (oldFsEntry.fullPath !== newFsEntry.fullPath) { + baseline.push(`//// [${file}] symlink was renamed from symlink ${oldFsEntry.fullPath}`); + } + else if (writtenFiles && !writtenFiles.has(newFsEntry.path)) { + baseline.push(`//// [${file}] symlink changed its modified time`); + } + else { + baseline.push(`//// [${file}] symlink written with same link`); + } + } + } + else { + baseline.push(`//// [${oldFsEntry.fullPath}] deleted symlink`); + if (isFsFile(newFsEntry)) { + diffFsFile(baseline, newFsEntry); + } + } + } + else if (isFsFile(newFsEntry)) { + diffFsFile(baseline, newFsEntry); + } + else if (isFsSymLink(newFsEntry)) { + diffFsSymLink(baseline, newFsEntry); + } + } + function serializeTestFsWatcher({ directoryName, fallbackPollingInterval, fallbackOptions }: VirtualFsWatcher) { return { directoryName, diff --git a/src/testRunner/unittests/tsbuild/sample.ts b/src/testRunner/unittests/tsbuild/sample.ts index e42408a1cd2..32010a7c086 100644 --- a/src/testRunner/unittests/tsbuild/sample.ts +++ b/src/testRunner/unittests/tsbuild/sample.ts @@ -234,7 +234,7 @@ namespace ts { const testsConfig = getTsBuildProjectFile("tests", "tsconfig.json"); const testsIndex = getTsBuildProjectFile("tests", "index.ts"); const baseline: string[] = []; - let oldSnap: ReturnType | undefined; + let oldSnap: ReturnType | undefined; const system = VirtualFS.changeToHostTrackingWrittenFiles( fakes.patchHostForBuildInfoReadWrite( tscWatch.createWatchedSystem([ @@ -265,9 +265,9 @@ namespace ts { function baselineState() { system.serializeOutput(baseline); - system.diff(baseline, oldSnap); + VirtualFS.diff(system, baseline, oldSnap); system.writtenFiles.clear(); - oldSnap = system.snap(); + oldSnap = VirtualFS.snap(system); } }); @@ -305,7 +305,7 @@ namespace ts { const testsConfig = getTsBuildProjectFile("tests", "tsconfig.json"); const testsIndex = getTsBuildProjectFile("tests", "index.ts"); const baseline: string[] = []; - let oldSnap: ReturnType | undefined; + let oldSnap: ReturnType | undefined; const system = VirtualFS.changeToHostTrackingWrittenFiles( fakes.patchHostForBuildInfoReadWrite( tscWatch.createWatchedSystem([ @@ -349,9 +349,9 @@ namespace ts { function baselineState(heading: string) { baseline.push(heading); system.serializeOutput(baseline); - system.diff(baseline, oldSnap); + VirtualFS.diff(system, baseline, oldSnap); system.writtenFiles.clear(); - oldSnap = system.snap(); + oldSnap = VirtualFS.snap(system); } }); }); diff --git a/src/testRunner/unittests/tscWatch/helpers.ts b/src/testRunner/unittests/tscWatch/helpers.ts index 60f1d351904..9d8a666a86a 100644 --- a/src/testRunner/unittests/tscWatch/helpers.ts +++ b/src/testRunner/unittests/tscWatch/helpers.ts @@ -129,7 +129,7 @@ namespace ts.tscWatch { timeouts: sys => sys.checkTimeoutQueueLength(0), }; - export type SystemSnap = ReturnType; + export type SystemSnap = ReturnType; function tscWatchCompile(input: TscWatchCompile) { it("tsc-watch:: Generates files matching the baseline", () => { const { sys, baseline, oldSnap } = createBaseline(input.sys()); @@ -178,9 +178,9 @@ namespace ts.tscWatch { const sys = VirtualFS.changeToHostTrackingWrittenFiles(initialSys); const baseline: string[] = []; baseline.push("Input::"); - sys.diff(baseline); + VirtualFS.diff(sys, baseline); const { cb, getPrograms } = commandLineCallbacks(sys); - return { sys, baseline, oldSnap: sys.snap(), cb, getPrograms }; + return { sys, baseline, oldSnap: VirtualFS.snap(sys), cb, getPrograms }; } export function createSolutionBuilderWithWatchHostForBaseline(sys: WatchedSystem, cb: ExecuteCommandLineCallbacks) { @@ -238,12 +238,12 @@ namespace ts.tscWatch { } export function applyChange(sys: BaselineBase["sys"], baseline: BaselineBase["baseline"], change: TscWatchCompileChange["change"], caption?: TscWatchCompileChange["caption"]) { - const oldSnap = sys.snap(); + const oldSnap = VirtualFS.snap(sys); baseline.push(`Change::${caption ? " " + caption : ""}`, ""); change(sys); baseline.push("Input::"); - sys.diff(baseline, oldSnap); - return sys.snap(); + VirtualFS.diff(sys, baseline, oldSnap); + return VirtualFS.snap(sys); } export interface RunWatchBaseline extends BaselineBase, TscWatchCompileBase { @@ -302,7 +302,7 @@ namespace ts.tscWatch { const programs = baselinePrograms(baseline, getPrograms, oldPrograms, baselineDependencies); sys.serializeWatches(baseline); baseline.push(`exitCode:: ExitStatus.${ExitStatus[sys.exitCode as ExitStatus]}`, ""); - sys.diff(baseline, oldSnap); + VirtualFS.diff(sys, baseline, oldSnap); sys.writtenFiles.forEach((value, key) => { assert.equal(value, 1, `Expected to write file ${key} only once`); }); diff --git a/src/testRunner/unittests/tscWatch/programUpdates.ts b/src/testRunner/unittests/tscWatch/programUpdates.ts index a17f857024f..ccdc99ff7d5 100644 --- a/src/testRunner/unittests/tscWatch/programUpdates.ts +++ b/src/testRunner/unittests/tscWatch/programUpdates.ts @@ -545,7 +545,7 @@ export class A { }); const {cb: cb2, getPrograms: getPrograms2 } = commandLineCallbacks(sys); - const oldSnap2 = sys.snap(); + const oldSnap2 = VirtualFS.snap(sys); baseline.push("createing separate watcher"); createWatchProgram(createWatchCompilerHostOfFilesAndCompilerOptionsForBaseline({ rootFiles:[file1.path], diff --git a/src/testRunner/unittests/tscWatch/watchApi.ts b/src/testRunner/unittests/tscWatch/watchApi.ts index fb2c582d59e..f0b1cbc9420 100644 --- a/src/testRunner/unittests/tscWatch/watchApi.ts +++ b/src/testRunner/unittests/tscWatch/watchApi.ts @@ -210,7 +210,7 @@ namespace ts.tscWatch { ) { const { cb, getPrograms } = commandLineCallbacks(sys); baseline.push(`tsc --w${optionsToExtend?.noEmit ? " --noEmit" : ""}`); - const oldSnap = sys.snap(); + const oldSnap = VirtualFS.snap(sys); const host = createWatchCompilerHostOfConfigFileForBaseline({ configFileName: config.path, optionsToExtend, @@ -385,7 +385,7 @@ namespace ts.tscWatch { applyChange(sys, baseline, sys => sys.writeFile(mainFile.path, "export const x = 10;"), "Fix error"); const { cb, getPrograms } = commandLineCallbacks(sys); - const oldSnap = sys.snap(); + const oldSnap = VirtualFS.snap(sys); const reportDiagnostic = createDiagnosticReporter(sys, /*pretty*/ true); const reportWatchStatus = createWatchStatusReporter(sys, /*pretty*/ true); const host = createWatchCompilerHostOfConfigFile({ diff --git a/src/testRunner/unittests/tsserver/updateFileSystem.ts b/src/testRunner/unittests/tsserver/updateFileSystem.ts index e283db6e6e8..4a4d6bb271b 100644 --- a/src/testRunner/unittests/tsserver/updateFileSystem.ts +++ b/src/testRunner/unittests/tsserver/updateFileSystem.ts @@ -41,13 +41,13 @@ ${file.fileContent}`; } function baselineFileSystem(scenario: string, subScenario: string, requests: [string, Partial][], host: VirtualFS.VirtualServerHost, session: TestSession) { const history: string[] = []; - let prev = host.snap(); + let prev = VirtualFS.snap(host); for (const [name, request] of requests) { session.executeCommandSeq(request); history.push(""); history.push("#### " + name); - host.diff(history, prev); - prev = host.snap(); + VirtualFS.diff(host, history, prev); + prev = VirtualFS.snap(host); } Harness.Baseline.runBaseline(`tsserver/${scenario}/${subScenario.split(" ").join("-")}.txt`, history.join("\r\n")); baselineTsserverLogs(scenario, subScenario, session); diff --git a/src/vfs/virtualFileSystemWithWatch.ts b/src/vfs/virtualFileSystemWithWatch.ts index 5d26eb43145..d6c65bf0b38 100644 --- a/src/vfs/virtualFileSystemWithWatch.ts +++ b/src/vfs/virtualFileSystemWithWatch.ts @@ -125,7 +125,7 @@ namespace ts.VirtualFS { export class VirtualServerHost implements FormatDiagnosticsHost, ModuleResolutionHost { args: string[] = []; - protected fs: ESMap = new Map(); + fs: ESMap = new Map(); getCanonicalFileName: (s: string) => string; protected toPath: (f: string) => Path; @@ -593,6 +593,8 @@ namespace ts.VirtualFS { // Do nothing } + writtenFiles!: ESMap; + realpath(s: string): string { const fullPath = this.toNormalizedAbsolutePath(s); const path = this.toPath(fullPath); @@ -624,98 +626,5 @@ namespace ts.VirtualFS { getEnvironmentVariable(_name: string) { return ""; } - - snap(): ESMap { - const result = new Map(); - this.fs.forEach((value, key) => { - const cloneValue = clone(value); - if (isFsFolder(cloneValue)) { - cloneValue.entries = cloneValue.entries.map(clone) as SortedArray; - } - result.set(key, cloneValue); - }); - - return result; - } - - writtenFiles?: ESMap; - diff(baseline: string[], base: ESMap = new Map()) { - this.fs.forEach(newFsEntry => { - diffFsEntry(baseline, base.get(newFsEntry.path), newFsEntry, this.writtenFiles); - }); - base.forEach(oldFsEntry => { - const newFsEntry = this.fs.get(oldFsEntry.path); - if (!newFsEntry) { - diffFsEntry(baseline, oldFsEntry, newFsEntry, this.writtenFiles); - } - }); - baseline.push(""); - } - } - - function diffFsFile(baseline: string[], fsEntry: FsFile) { - baseline.push(`//// [${fsEntry.fullPath}]\r\n${fsEntry.content}`, ""); - } - - function diffFsSymLink(baseline: string[], fsEntry: FsSymLink) { - baseline.push(`//// [${fsEntry.fullPath}] symlink(${fsEntry.symLink})`); - } - - function diffFsEntry(baseline: string[], oldFsEntry: FSEntry | undefined, newFsEntry: FSEntry | undefined, writtenFiles: ESMap | undefined): void { - const file = newFsEntry && newFsEntry.fullPath; - if (isFsFile(oldFsEntry)) { - if (isFsFile(newFsEntry)) { - if (oldFsEntry.content !== newFsEntry.content) { - diffFsFile(baseline, newFsEntry); - } - else if (oldFsEntry.modifiedTime !== newFsEntry.modifiedTime) { - if (oldFsEntry.fullPath !== newFsEntry.fullPath) { - baseline.push(`//// [${file}] file was renamed from file ${oldFsEntry.fullPath}`); - } - else if (writtenFiles && !writtenFiles.has(newFsEntry.path)) { - baseline.push(`//// [${file}] file changed its modified time`); - } - else { - baseline.push(`//// [${file}] file written with same contents`); - } - } - } - else { - baseline.push(`//// [${oldFsEntry.fullPath}] deleted`); - if (isFsSymLink(newFsEntry)) { - diffFsSymLink(baseline, newFsEntry); - } - } - } - else if (isFsSymLink(oldFsEntry)) { - if (isFsSymLink(newFsEntry)) { - if (oldFsEntry.symLink !== newFsEntry.symLink) { - diffFsSymLink(baseline, newFsEntry); - } - else if (oldFsEntry.modifiedTime !== newFsEntry.modifiedTime) { - if (oldFsEntry.fullPath !== newFsEntry.fullPath) { - baseline.push(`//// [${file}] symlink was renamed from symlink ${oldFsEntry.fullPath}`); - } - else if (writtenFiles && !writtenFiles.has(newFsEntry.path)) { - baseline.push(`//// [${file}] symlink changed its modified time`); - } - else { - baseline.push(`//// [${file}] symlink written with same link`); - } - } - } - else { - baseline.push(`//// [${oldFsEntry.fullPath}] deleted symlink`); - if (isFsFile(newFsEntry)) { - diffFsFile(baseline, newFsEntry); - } - } - } - else if (isFsFile(newFsEntry)) { - diffFsFile(baseline, newFsEntry); - } - else if (isFsSymLink(newFsEntry)) { - diffFsSymLink(baseline, newFsEntry); - } } }