Skip to content

Commit 554a1dc

Browse files
test: add regression tests for memory leaks in test runner
1 parent 6d5c9d1 commit 554a1dc

File tree

1 file changed

+99
-0
lines changed

1 file changed

+99
-0
lines changed
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
'use strict';
2+
3+
// Regression test for memory leaks in test runner
4+
// Without the fix, these tests could hang or accumulate resources
5+
6+
const common = require('../common');
7+
const assert = require('assert');
8+
const { spawn } = require('child_process');
9+
const { writeFileSync } = require('fs');
10+
const { join } = require('path');
11+
const tmpdir = require('../common/tmpdir');
12+
13+
tmpdir.refresh();
14+
15+
// Test 1: Verify readline interface cleanup
16+
// Without fix: readline.Interface remains open when child has stderr output
17+
// This could cause the process to hang or resources to leak
18+
{
19+
const testFile = join(tmpdir.path, 'test-with-stderr.js');
20+
writeFileSync(
21+
testFile,
22+
`
23+
const test = require('node:test');
24+
test('test with stderr', () => {
25+
process.stderr.write('line1\\n');
26+
process.stderr.write('line2\\n');
27+
});
28+
`
29+
);
30+
31+
const child = spawn(process.execPath, [
32+
'--test',
33+
'--test-isolation=process',
34+
testFile,
35+
]);
36+
37+
let stdout = '';
38+
child.stdout.on('data', (chunk) => {
39+
stdout += chunk;
40+
});
41+
42+
child.on(
43+
'close',
44+
common.mustCall((code) => {
45+
assert.strictEqual(code, 0);
46+
assert.match(stdout, /tests 1/);
47+
assert.match(stdout, /pass 1/);
48+
})
49+
);
50+
51+
// Without fix: process might not exit cleanly due to unclosed readline
52+
const timeout = setTimeout(() => {
53+
if (!child.killed) {
54+
child.kill();
55+
assert.fail('Process did not exit within timeout');
56+
}
57+
}, 30000);
58+
timeout.unref();
59+
}
60+
61+
// Test 2: Verify watch mode can be stopped cleanly
62+
// Without fix: watch mode listener might not be removed on abort
63+
{
64+
const testFile = join(tmpdir.path, 'test-watch.js');
65+
writeFileSync(
66+
testFile,
67+
`
68+
const test = require('node:test');
69+
test('watch test', () => {});
70+
`
71+
);
72+
73+
const child = spawn(process.execPath, ['--test', '--watch', testFile]);
74+
75+
let stdout = '';
76+
child.stdout.on('data', (chunk) => {
77+
stdout += chunk;
78+
if (stdout.includes('pass 1')) {
79+
child.kill('SIGTERM');
80+
}
81+
});
82+
83+
child.on(
84+
'close',
85+
common.mustCall(() => {
86+
assert.match(stdout, /tests 1/);
87+
assert.match(stdout, /pass 1/);
88+
})
89+
);
90+
91+
// Without fix: process might not respond to SIGTERM properly
92+
const timeout = setTimeout(() => {
93+
if (!child.killed) {
94+
child.kill('SIGKILL');
95+
assert.fail('Watch mode did not exit cleanly on SIGTERM');
96+
}
97+
}, 10000);
98+
timeout.unref();
99+
}

0 commit comments

Comments
 (0)