[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) <aab@odoo.com>
This commit is contained in:
Samuel Degueldre
2023-08-02 06:18:09 +02:00
parent 182c8b0b84
commit c4b7d65e2e
+32 -20
View File
@@ -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();