52 lines
2.3 KiB
JavaScript
52 lines
2.3 KiB
JavaScript
import { test } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { spawn } from 'node:child_process';
|
|
import { once } from 'node:events';
|
|
import { Client } from './client.mjs';
|
|
|
|
test('closing an exited browser releases stderr even when a helper inherited its pipe', { timeout: 5000 }, async () => {
|
|
const child = spawn(process.execPath, ['-e', `
|
|
const { spawn } = require('node:child_process');
|
|
const helper = spawn(process.execPath, ['-e', 'setTimeout(() => {}, 5000)'],
|
|
{ stdio: ['ignore', 'ignore', 2], detached: true });
|
|
helper.unref();
|
|
process.send({ pid: helper.pid }, () => process.exit(0));
|
|
`], { stdio: ['ignore', 'pipe', 'pipe', 'ipc'] });
|
|
const exited = once(child, 'exit');
|
|
let helperPid;
|
|
try {
|
|
[{ pid: helperPid }] = await once(child, 'message');
|
|
child.stderr.resume();
|
|
await exited;
|
|
assert.equal(child.exitCode, 0);
|
|
assert.equal(child.stderr.destroyed, false, 'the helper still holds the other end of stderr');
|
|
const client = new Client(null, '', 'cleanup');
|
|
client.browser = { process: () => child, close: async () => {} };
|
|
await client.close();
|
|
assert.equal(child.stderr.destroyed, true, 'the exited browser no longer keeps this test alive');
|
|
} finally {
|
|
if (helperPid) { try { process.kill(helperPid, 'SIGTERM'); } catch { /* already exited */ } }
|
|
for (const stream of child.stdio) stream?.destroy?.();
|
|
if (child.exitCode == null && child.signalCode == null) child.kill();
|
|
}
|
|
});
|
|
|
|
test('a failed shutdown leaves a live browser and its pipes alone', async () => {
|
|
let destroyed = 0;
|
|
const child = { exitCode: null, signalCode: null, stdio: [{ destroy: () => destroyed++ }] };
|
|
const client = new Client(null, '', 'cleanup');
|
|
client.browser = { process: () => child, close: async () => { throw new Error('not closed'); } };
|
|
await client.close();
|
|
assert.equal(destroyed, 0);
|
|
});
|
|
|
|
test('a browser terminated by a signal releases only its owned pipes; an absent browser is harmless', async () => {
|
|
let destroyed = 0;
|
|
const client = new Client(null, '', 'cleanup');
|
|
await client.close();
|
|
client.browser = { process: () => ({ exitCode: null, signalCode: 'SIGTERM', stdio: [null, { destroy: () => destroyed++ }] }),
|
|
close: async () => {} };
|
|
await client.close();
|
|
assert.equal(destroyed, 1);
|
|
});
|