diff --git a/test/automation/src/playwrightDriver.ts b/test/automation/src/playwrightDriver.ts index 59d012879f5..681ca4ef540 100644 --- a/test/automation/src/playwrightDriver.ts +++ b/test/automation/src/playwrightDriver.ts @@ -110,6 +110,14 @@ export class PlaywrightDriver { return await this._cdpSession.send('Runtime.evaluate', options); } + async releaseObjectGroup(parameters: Protocol.Runtime.releaseObjectGroupParameters): Promise { + if (!this._cdpSession) { + throw new Error('CDP not started'); + } + + await this._cdpSession.send('Runtime.releaseObjectGroup', parameters); + } + async queryObjects(parameters: Protocol.Runtime.queryObjectsParameters): Promise { if (!this._cdpSession) { throw new Error('CDP not started'); @@ -132,11 +140,15 @@ export class PlaywrightDriver { } let snapshot = ''; - this._cdpSession.addListener('HeapProfiler.addHeapSnapshotChunk', ({ chunk }) => { - snapshot += chunk; - }); + const listener = (c: { chunk: string }) => { + snapshot += c.chunk; + }; + + this._cdpSession.addListener('HeapProfiler.addHeapSnapshotChunk', listener); await this._cdpSession.send('HeapProfiler.takeHeapSnapshot'); + + this._cdpSession.removeListener('HeapProfiler.addHeapSnapshotChunk', listener); return snapshot; } diff --git a/test/automation/src/profiler.ts b/test/automation/src/profiler.ts index d6f339c3030..1bf9c0c0bd0 100644 --- a/test/automation/src/profiler.ts +++ b/test/automation/src/profiler.ts @@ -12,26 +12,28 @@ export class Profiler { constructor(private readonly code: Code) { } - async checkLeaks(classNames: string | string[], fn: () => Promise): Promise { + async checkObjectLeaks(classNames: string | string[], fn: () => Promise): Promise { await this.code.driver.startCDP(); const countsBefore: { [key: string]: number } = {}; const instancesBefore = await getInstances(this.code.driver); const classNamesArray = Array.isArray(classNames) ? classNames : [classNames]; for (const className of classNamesArray) { - const matchedInstances = instancesBefore.find(e => e.name !== undefined && e.name === className); + const matchedInstances = instancesBefore.value.find(e => e.name !== undefined && e.name === className); if (!matchedInstances) { throw new Error(`${className} not found`); } countsBefore[className] = matchedInstances.count; } + await instancesBefore.dispose(); + await fn(); const instancesAfter = await getInstances(this.code.driver); const leaks: string[] = []; for (const className of classNamesArray) { - const matchedInstancesAfter = instancesAfter.find(e => e.name !== undefined && e.name === className); + const matchedInstancesAfter = instancesAfter.value.find(e => e.name !== undefined && e.name === className); if (!matchedInstancesAfter) { throw new Error(`${className} not found`); } @@ -42,6 +44,8 @@ export class Profiler { } } + await instancesAfter.dispose(); + if (leaks.length > 0) { throw new Error(leaks.join('\n')); } @@ -126,7 +130,7 @@ function generateUuid() { * This code is derived from https://github.com/SimonSiefke/vscode-memory-leak-finder *--------------------------------------------------------------------------------------------*/ -const getInstances = async (driver: PlaywrightDriver): Promise> => { +const getInstances = async (driver: PlaywrightDriver): Promise<{ value: Array<{ name: string; count: number }>; dispose: () => Promise }> => { await driver.collectGarbage(); const objectGroup = `og:${generateUuid()}`; const prototypeDescriptor = await driver.evaluate({ @@ -207,7 +211,13 @@ const getInstances = async (driver: PlaywrightDriver): Promise { + // release object group + await driver.releaseObjectGroup({ objectGroup: objectGroup }); + } + }; }; const getInstanceCountMap = async (driver: PlaywrightDriver, objectGroup: string, objects: Protocol.Runtime.RemoteObject) => { diff --git a/test/smoke/src/areas/notebook/notebook.test.ts b/test/smoke/src/areas/notebook/notebook.test.ts index 2de6bd77cf6..da4d527fe4f 100644 --- a/test/smoke/src/areas/notebook/notebook.test.ts +++ b/test/smoke/src/areas/notebook/notebook.test.ts @@ -25,7 +25,7 @@ export function setup(logger: Logger) { cp.execSync('git reset --hard HEAD --quiet', { cwd: app.workspacePathOrFolder }); }); - it('check heap leaks', async function () { + it.skip('check heap leaks', async function () { const app = this.app as Application; await app.profiler.checkHeapLeaks(['NotebookTextModel', 'NotebookCellTextModel', 'NotebookEventDispatcher'], async () => { await app.workbench.notebook.openNotebook(); @@ -36,7 +36,7 @@ export function setup(logger: Logger) { it('check object leaks', async function () { const app = this.app as Application; - await app.profiler.checkLeaks(['NotebookTextModel'], async () => { + await app.profiler.checkObjectLeaks(['NotebookTextModel', 'NotebookCellTextModel', 'NotebookEventDispatcher'], async () => { await app.workbench.notebook.openNotebook(); await app.workbench.quickaccess.runCommand('workbench.action.files.save'); await app.workbench.quickaccess.runCommand('workbench.action.closeActiveEditor');