From c4b7d65e2efa737fa636c1e5014c6913b118fa44 Mon Sep 17 00:00:00 2001 From: Samuel Degueldre Date: Tue, 1 Aug 2023 10:47:31 +0000 Subject: [PATCH] [FIX] web: stop leaking event listeners on EventBus in QUnit suite In odoo/odoo#120893, the mail team changed their test utils so that they would always start all services. This can cause issues because services do not have any built-in destruction/cleanup mechanism, as in a production application they are only supposed to be started once. Some services add event listeners on global objects, in particular, the tour service adds an event listener on the tour registry which is global. If this event listener is not cleaned up, every time the tour service is started, a reference to the environment in which this tour service is being started is leaked. Since the environment contains all started services, it is typically a pretty heavy object, especially since in this context, all services are started instead of a select subset. This commit fixes that by patching the EventBus class to automatically remove event listeners that were added on an event bus after the test, in a similar fashion to what we do for some global objects like window or the document's body. closes odoo/odoo#130373 Signed-off-by: Aaron Bohy (aab) --- addons/web/static/tests/setup.js | 52 ++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/addons/web/static/tests/setup.js b/addons/web/static/tests/setup.js index 81236c1f7dc..cfc00cfdba8 100644 --- a/addons/web/static/tests/setup.js +++ b/addons/web/static/tests/setup.js @@ -16,7 +16,7 @@ import { loadLanguages } from "@web/core/l10n/translation"; transitionConfig.disabled = true; import { patch } from "@web/core/utils/patch"; -import { App, whenReady } from "@odoo/owl"; +import { App, EventBus, whenReady } from "@odoo/owl"; import { currencies } from "@web/core/currency"; import "./helpers/session"; @@ -352,27 +352,38 @@ function patchAssets() { }); } +function patchEventBus() { + patchWithCleanup(EventBus.prototype, { + addEventListener() { + this._super(...arguments); + registerCleanup(() => this.removeEventListener(...arguments)); + }, + }); +} + export async function setupTests() { // uncomment to debug memory leaks in qunit suite - // let memoryBeforeModule; - // QUnit.moduleStart(({ tests }) => { - // if (tests.length) { - // window.gc(); - // memoryBeforeModule = window.performance.memory.usedJSHeapSize; - // } - // }); - // QUnit.moduleDone(({ name }) => { - // if (memoryBeforeModule) { - // window.gc(); - // const afterGc = window.performance.memory.usedJSHeapSize; - // console.log( - // `MEMINFO - After suite "${name}" - after gc: ${afterGc} delta: ${ - // afterGc - memoryBeforeModule - // }` - // ); - // memoryBeforeModule = null; - // } - // }); + // if (window.gc) { + // let memoryBeforeModule; + // QUnit.moduleStart(({ tests }) => { + // if (tests.length) { + // window.gc(); + // memoryBeforeModule = window.performance.memory.usedJSHeapSize; + // } + // }); + // QUnit.moduleDone(({ name }) => { + // if (memoryBeforeModule) { + // window.gc(); + // const afterGc = window.performance.memory.usedJSHeapSize; + // console.log( + // `MEMINFO - After suite "${name}" - after gc: ${afterGc} delta: ${ + // afterGc - memoryBeforeModule + // }` + // ); + // memoryBeforeModule = null; + // } + // }); + // } QUnit.testStart(() => { checkGlobalObjectsIntegrity(); @@ -381,6 +392,7 @@ export async function setupTests() { cleanLoadedLanguages(); patchBrowserWithCleanup(); patchBodyAddEventListener(); + patchEventBus(); patchLegacyBus(); patchOdoo(); patchSessionInfo();