diff --git a/src/harness/unittests/telemetry.ts b/src/harness/unittests/telemetry.ts index 25120af45c1..9bb2db73801 100644 --- a/src/harness/unittests/telemetry.ts +++ b/src/harness/unittests/telemetry.ts @@ -5,9 +5,9 @@ namespace ts.projectSystem { describe("project telemetry", () => { it("does nothing for inferred project", () => { const file = makeFile("/a.js"); - const et = new EventTracker([file]); + const et = new TestServerEventManager([file]); et.service.openClientFile(file.path); - assert.equal(et.getEventsWithName(ts.server.ProjectInfoTelemetryEvent).length, 0); + et.hasZeroEvent(ts.server.ProjectInfoTelemetryEvent); }); it("only sends an event once", () => { @@ -15,7 +15,7 @@ namespace ts.projectSystem { const file2 = makeFile("/b.ts"); const tsconfig = makeFile("/a/tsconfig.json", {}); - const et = new EventTracker([file, file2, tsconfig]); + const et = new TestServerEventManager([file, file2, tsconfig]); et.service.openClientFile(file.path); et.assertProjectInfoTelemetryEvent({}, tsconfig.path); @@ -25,12 +25,12 @@ namespace ts.projectSystem { et.service.openClientFile(file2.path); checkNumberOfProjects(et.service, { inferredProjects: 1 }); - assert.equal(et.getEventsWithName(ts.server.ProjectInfoTelemetryEvent).length, 0); + et.hasZeroEvent(ts.server.ProjectInfoTelemetryEvent); et.service.openClientFile(file.path); checkNumberOfProjects(et.service, { configuredProjects: 1, inferredProjects: 1 }); - assert.equal(et.getEventsWithName(ts.server.ProjectInfoTelemetryEvent).length, 0); + et.hasZeroEvent(ts.server.ProjectInfoTelemetryEvent); }); it("counts files by extension", () => { @@ -39,7 +39,7 @@ namespace ts.projectSystem { const compilerOptions: ts.CompilerOptions = { allowJs: true }; const tsconfig = makeFile("/tsconfig.json", { compilerOptions, include: ["src"] }); - const et = new EventTracker([...files, notIncludedFile, tsconfig]); + const et = new TestServerEventManager([...files, notIncludedFile, tsconfig]); et.service.openClientFile(files[0].path); et.assertProjectInfoTelemetryEvent({ fileStats: { ts: 2, tsx: 1, js: 1, jsx: 1, dts: 1 }, @@ -50,7 +50,7 @@ namespace ts.projectSystem { it("works with external project", () => { const file1 = makeFile("/a.ts"); - const et = new EventTracker([file1]); + const et = new TestServerEventManager([file1]); const compilerOptions: ts.server.protocol.CompilerOptions = { strict: true }; const projectFileName = "/hunter2/foo.csproj"; @@ -148,7 +148,7 @@ namespace ts.projectSystem { (compilerOptions as any).unknownCompilerOption = "hunter2"; // These are always ignored. const tsconfig = makeFile("/tsconfig.json", { compilerOptions, files: ["/a.ts"] }); - const et = new EventTracker([file, tsconfig]); + const et = new TestServerEventManager([file, tsconfig]); et.service.openClientFile(file.path); et.assertProjectInfoTelemetryEvent({ @@ -168,7 +168,7 @@ namespace ts.projectSystem { compileOnSave: true, }); - const et = new EventTracker([tsconfig, file]); + const et = new TestServerEventManager([tsconfig, file]); et.service.openClientFile(file.path); et.assertProjectInfoTelemetryEvent({ extends: true, @@ -198,7 +198,7 @@ namespace ts.projectSystem { exclude: [], }, }); - const et = new EventTracker([jsconfig, file]); + const et = new TestServerEventManager([jsconfig, file]); et.service.openClientFile(file.path); et.assertProjectInfoTelemetryEvent({ projectId: Harness.mockHash("/jsconfig.json"), @@ -216,7 +216,7 @@ namespace ts.projectSystem { it("detects whether language service was disabled", () => { const file = makeFile("/a.js"); const tsconfig = makeFile("/jsconfig.json", {}); - const et = new EventTracker([tsconfig, file]); + const et = new TestServerEventManager([tsconfig, file]); et.host.getFileSize = () => server.maxProgramSizeForNonTsFiles + 1; et.service.openClientFile(file.path); et.getEvent(server.ProjectLanguageServiceStateEvent); @@ -235,83 +235,7 @@ namespace ts.projectSystem { }); }); - class EventTracker { - private events: server.ProjectServiceEvent[] = []; - readonly service: TestProjectService; - readonly host: projectSystem.TestServerHost; - - constructor(files: projectSystem.FileOrFolder[]) { - this.host = createServerHost(files); - this.service = createProjectService(this.host, { - eventHandler: event => { - this.events.push(event); - }, - }); - } - - getEvents(): ReadonlyArray { - const events = this.events; - this.events = []; - return events; - } - - getEventsWithName(eventName: T["eventName"]): ReadonlyArray { - let events: T[]; - filterMutate(this.events, event => { - if (event.eventName === eventName) { - (events || (events = [])).push(event as T); - return false; - } - return true; - }); - return events || emptyArray; - } - - assertProjectInfoTelemetryEvent(partial: Partial, configFile?: string): void { - assert.deepEqual(this.getEvent(ts.server.ProjectInfoTelemetryEvent), { - projectId: Harness.mockHash(configFile || "/tsconfig.json"), - fileStats: fileStats({ ts: 1 }), - compilerOptions: {}, - extends: false, - files: false, - include: false, - exclude: false, - compileOnSave: false, - typeAcquisition: { - enable: false, - exclude: false, - include: false, - }, - configFileName: "tsconfig.json", - projectType: "configured", - languageServiceEnabled: true, - version: ts.version, - ...partial, - }); - } - - getEvent(eventName: T["eventName"]): T["data"] { - let event: server.ProjectServiceEvent; - filterMutate(this.events, e => { - if (e.eventName === eventName) { - if (event) { - assert(false, "more than one event found"); - } - event = e; - return false; - } - return true; - }); - assert.equal(event.eventName, eventName); - return event.data; - } - } - function makeFile(path: string, content: {} = ""): projectSystem.FileOrFolder { return { path, content: isString(content) ? "" : JSON.stringify(content) }; } - - function fileStats(nonZeroStats: Partial): server.FileStats { - return { ts: 0, tsx: 0, dts: 0, js: 0, jsx: 0, ...nonZeroStats }; - } } diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index 04b848e7cdf..d5b9b9d62fc 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -132,16 +132,78 @@ namespace ts.projectSystem { return map(fileNames, toExternalFile); } - class TestServerEventManager { - public events: server.ProjectServiceEvent[] = []; + export function fileStats(nonZeroStats: Partial): server.FileStats { + return { ts: 0, tsx: 0, dts: 0, js: 0, jsx: 0, ...nonZeroStats }; + } - handler: server.ProjectServiceEventHandler = (event: server.ProjectServiceEvent) => { - this.events.push(event); + export class TestServerEventManager { + private events: server.ProjectServiceEvent[] = []; + readonly session: TestSession; + readonly service: server.ProjectService; + readonly host: projectSystem.TestServerHost; + constructor(files: projectSystem.FileOrFolder[]) { + this.host = createServerHost(files); + this.session = createSession(this.host, { + canUseEvents: true, + eventHandler: event => this.events.push(event), + }); + this.service = this.session.getProjectService(); } - checkEventCountOfType(eventType: "configFileDiag", expectedCount: number) { - const eventsOfType = filter(this.events, e => e.eventName === eventType); - assert.equal(eventsOfType.length, expectedCount, `The actual event counts of type ${eventType} is ${eventsOfType.length}, while expected ${expectedCount}`); + getEvents(): ReadonlyArray { + const events = this.events; + this.events = []; + return events; + } + + getEvent(eventName: T["eventName"]): T["data"] { + let eventData: T["data"]; + filterMutate(this.events, e => { + if (e.eventName === eventName) { + if (eventData !== undefined) { + assert(false, "more than one event found"); + } + eventData = e.data; + return false; + } + return true; + }); + assert.isDefined(eventData); + return eventData; + } + + hasZeroEvent(eventName: T["eventName"]) { + const eventCount = countWhere(this.events, event => event.eventName === eventName); + assert.equal(eventCount, 0); + } + + checkSingleConfigFileDiagEvent(configFileName: string, triggerFile: string) { + const eventData = this.getEvent(server.ConfigFileDiagEvent); + assert.equal(eventData.configFileName, configFileName); + assert.equal(eventData.triggerFile, triggerFile); + } + + assertProjectInfoTelemetryEvent(partial: Partial, configFile?: string): void { + assert.deepEqual(this.getEvent(ts.server.ProjectInfoTelemetryEvent), { + projectId: Harness.mockHash(configFile || "/tsconfig.json"), + fileStats: fileStats({ ts: 1 }), + compilerOptions: {}, + extends: false, + files: false, + include: false, + exclude: false, + compileOnSave: false, + typeAcquisition: { + enable: false, + exclude: false, + include: false, + }, + configFileName: "tsconfig.json", + projectType: "configured", + languageServiceEnabled: true, + version: ts.version, + ...partial, + }); } } @@ -3076,7 +3138,6 @@ namespace ts.projectSystem { describe("Configure file diagnostics events", () => { it("are generated when the config file has errors", () => { - const serverEventManager = new TestServerEventManager(); const file = { path: "/a/b/app.ts", content: "let x = 10" @@ -3090,26 +3151,12 @@ namespace ts.projectSystem { } }` }; - - const host = createServerHost([file, configFile]); - const session = createSession(host, { - canUseEvents: true, - eventHandler: serverEventManager.handler - }); - openFilesForSession([file], session); - serverEventManager.checkEventCountOfType("configFileDiag", 1); - - for (const event of serverEventManager.events) { - if (event.eventName === "configFileDiag") { - assert.equal(event.data.configFileName, configFile.path); - assert.equal(event.data.triggerFile, file.path); - return; - } - } + const serverEventManager = new TestServerEventManager([file, configFile]); + openFilesForSession([file], serverEventManager.session); + serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, file.path); }); it("are generated when the config file doesn't have errors", () => { - const serverEventManager = new TestServerEventManager(); const file = { path: "/a/b/app.ts", content: "let x = 10" @@ -3120,18 +3167,12 @@ namespace ts.projectSystem { "compilerOptions": {} }` }; - - const host = createServerHost([file, configFile]); - const session = createSession(host, { - canUseEvents: true, - eventHandler: serverEventManager.handler - }); - openFilesForSession([file], session); - serverEventManager.checkEventCountOfType("configFileDiag", 1); + const serverEventManager = new TestServerEventManager([file, configFile]); + openFilesForSession([file], serverEventManager.session); + serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, file.path); }); it("are generated when the config file changes", () => { - const serverEventManager = new TestServerEventManager(); const file = { path: "/a/b/app.ts", content: "let x = 10" @@ -3143,33 +3184,28 @@ namespace ts.projectSystem { }` }; - const host = createServerHost([file, configFile]); - const session = createSession(host, { - canUseEvents: true, - eventHandler: serverEventManager.handler - }); - openFilesForSession([file], session); - serverEventManager.checkEventCountOfType("configFileDiag", 1); + const serverEventManager = new TestServerEventManager([file, configFile]); + openFilesForSession([file], serverEventManager.session); + serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, file.path); configFile.content = `{ "compilerOptions": { "haha": 123 } }`; - host.reloadFS([file, configFile]); - host.runQueuedTimeoutCallbacks(); - serverEventManager.checkEventCountOfType("configFileDiag", 2); + serverEventManager.host.reloadFS([file, configFile]); + serverEventManager.host.runQueuedTimeoutCallbacks(); + serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, configFile.path); configFile.content = `{ "compilerOptions": {} }`; - host.reloadFS([file, configFile]); - host.runQueuedTimeoutCallbacks(); - serverEventManager.checkEventCountOfType("configFileDiag", 3); + serverEventManager.host.reloadFS([file, configFile]); + serverEventManager.host.runQueuedTimeoutCallbacks(); + serverEventManager.checkSingleConfigFileDiagEvent(configFile.path, configFile.path); }); it("are not generated when the config file doesnot include file opened and config file has errors", () => { - const serverEventManager = new TestServerEventManager(); const file = { path: "/a/b/app.ts", content: "let x = 10" @@ -3188,18 +3224,12 @@ namespace ts.projectSystem { "files": ["app.ts"] }` }; - - const host = createServerHost([file, file2, libFile, configFile]); - const session = createSession(host, { - canUseEvents: true, - eventHandler: serverEventManager.handler - }); - openFilesForSession([file2], session); - serverEventManager.checkEventCountOfType("configFileDiag", 0); + const serverEventManager = new TestServerEventManager([file, file2, libFile, configFile]); + openFilesForSession([file2], serverEventManager.session); + serverEventManager.hasZeroEvent("configFileDiag"); }); it("are not generated when the config file doesnot include file opened and doesnt contain any errors", () => { - const serverEventManager = new TestServerEventManager(); const file = { path: "/a/b/app.ts", content: "let x = 10" @@ -3215,13 +3245,9 @@ namespace ts.projectSystem { }` }; - const host = createServerHost([file, file2, libFile, configFile]); - const session = createSession(host, { - canUseEvents: true, - eventHandler: serverEventManager.handler - }); - openFilesForSession([file2], session); - serverEventManager.checkEventCountOfType("configFileDiag", 0); + const serverEventManager = new TestServerEventManager([file, file2, libFile, configFile]); + openFilesForSession([file2], serverEventManager.session); + serverEventManager.hasZeroEvent("configFileDiag"); }); });