diff --git a/lib/fork.js b/lib/fork.js index 8317774ff..82d12ffc3 100644 --- a/lib/fork.js +++ b/lib/fork.js @@ -104,6 +104,11 @@ export default function loadFork(file, options, execArgv = process.execArgv) { } switch (message.ava.type) { + case 'worker-finished': { + send({type: 'free-worker'}); + break; + } + case 'ready-for-options': { send({type: 'options', options}); break; diff --git a/lib/worker/base.js b/lib/worker/base.js index 520107dd3..e3d3a3131 100644 --- a/lib/worker/base.js +++ b/lib/worker/base.js @@ -120,6 +120,15 @@ const run = async options => { return; } + channel.send({type: 'worker-finished'}); + + // Reference the channel until the worker is freed. This should prevent Node.js from terminating the child process + // prematurely, which has been witnessed on Windows. See discussion at + // . + channel.ref(); + await channel.workerFreed; + channel.unref(); + nowAndTimers.setImmediate(() => { const unhandled = currentlyUnhandled(); if (unhandled.length === 0) { diff --git a/lib/worker/channel.cjs b/lib/worker/channel.cjs index 0479eeca6..177bba359 100644 --- a/lib/worker/channel.cjs +++ b/lib/worker/channel.cjs @@ -107,7 +107,9 @@ handle.ref(); exports.options = selectAvaMessage(handle.channel, 'options').then(message => message.ava.options); exports.peerFailed = selectAvaMessage(handle.channel, 'peer-failed'); +exports.workerFreed = selectAvaMessage(handle.channel, 'free-worker'); exports.send = handle.send.bind(handle); +exports.ref = handle.ref.bind(handle); exports.unref = handle.unref.bind(handle); let channelCounter = 0; diff --git a/test/child-process/fixtures/package.json b/test/child-process/fixtures/package.json new file mode 100644 index 000000000..bedb411a9 --- /dev/null +++ b/test/child-process/fixtures/package.json @@ -0,0 +1,3 @@ +{ + "type": "module" +} diff --git a/test/child-process/fixtures/test.js b/test/child-process/fixtures/test.js new file mode 100644 index 000000000..f7c5e8175 --- /dev/null +++ b/test/child-process/fixtures/test.js @@ -0,0 +1,7 @@ +import test from 'ava'; + +for (let i = 0; i < 50; i++) { + test('Test ' + (i + 1), t => { + t.true(true); + }); +} diff --git a/test/child-process/test.js b/test/child-process/test.js new file mode 100644 index 000000000..e75cb29a7 --- /dev/null +++ b/test/child-process/test.js @@ -0,0 +1,9 @@ +import test from '@ava/test'; + +import {fixture} from '../helpers/exec.js'; + +// Regression test for https://github.com/avajs/ava/issues/3390 +test('running 50 tests in a child process works as expected', async t => { + const result = await fixture(['--no-worker-threads']); + t.is(result.stats.passed.length, 50); +});