Skip to content

Commit 1efdb58

Browse files
abhishekSavanijuanarbol
authored andcommitted
test_runner: fix memory leaks in runner
- Close readline interface after child process exits Prevents accumulation of event listeners on stderr stream - Extract watch mode event handler to named function Allows proper cleanup when watch mode is aborted These changes prevent unbounded memory growth in long-running test suites and watch mode sessions. PR-URL: #60860 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Moshe Atlow <moshe@atlow.co.il>
1 parent cba42aa commit 1efdb58

1 file changed

Lines changed: 19 additions & 6 deletions

File tree

lib/internal/test_runner/runner.js

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,9 @@ function runTestFile(path, filesWatcher, opts) {
422422
finished(child.stdout, { __proto__: null, signal: t.signal }),
423423
]);
424424

425+
// Close readline interface to prevent memory leak
426+
rl.close();
427+
425428
if (watchMode) {
426429
filesWatcher.runningProcesses.delete(path);
427430
filesWatcher.runningSubtests.delete(path);
@@ -483,7 +486,7 @@ function watchFiles(testFiles, opts) {
483486
}
484487

485488
// Watch for changes in current filtered files
486-
watcher.on('changed', ({ owners, eventType }) => {
489+
const onChanged = ({ owners, eventType }) => {
487490
if (!opts.hasFiles && (eventType === 'rename' || eventType === 'change')) {
488491
const updatedTestFiles = createTestFileList(opts.globPatterns, opts.cwd);
489492
const newFileName = ArrayPrototypeFind(updatedTestFiles, (x) => !ArrayPrototypeIncludes(testFiles, x));
@@ -518,19 +521,29 @@ function watchFiles(testFiles, opts) {
518521
triggerUncaughtException(error, true /* fromPromise */);
519522
}));
520523
}
521-
});
524+
};
525+
526+
watcher.on('changed', onChanged);
527+
528+
// Cleanup function to remove event listener and prevent memory leak
529+
const cleanup = () => {
530+
watcher.removeListener('changed', onChanged);
531+
opts.root.harness.watching = false;
532+
opts.root.postRun();
533+
};
534+
522535
if (opts.signal) {
523536
kResistStopPropagation ??= require('internal/event_target').kResistStopPropagation;
524537
opts.signal.addEventListener(
525538
'abort',
526-
() => {
527-
opts.root.harness.watching = false;
528-
opts.root.postRun();
529-
},
539+
cleanup,
530540
{ __proto__: null, once: true, [kResistStopPropagation]: true },
531541
);
532542
}
533543

544+
// Expose cleanup method for proper resource management
545+
filesWatcher.cleanup = cleanup;
546+
534547
return filesWatcher;
535548
}
536549

0 commit comments

Comments
 (0)