Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add filterNodeArgumentsForWorkerThreads option #3336

Merged
merged 14 commits into from
Aug 20, 2024
16 changes: 16 additions & 0 deletions docs/06-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -337,3 +337,19 @@ These may also export a function which is then invoked, and can receive argument
The `nodeArguments` configuration may be used to specify additional arguments for launching worker processes. These are combined with `--node-arguments` passed on the CLI and any arguments passed to the `node` binary when starting AVA.

[CLI]: ./05-command-line.md

## Node arguments filter for worker threads

In a config file only, `filterNodeArgumentsForWorkerThreads` may provide a function used for filtering `nodeArguments` sent to worker threads. This enables excluding arguments that throw if sent to a thread. The filter is ignored by worker processes.

`ava.config.js`:
```js
This conversation was marked as resolved.
Show resolved Hide resolved
const processOnly = new Set([
'--allow-natives-syntax',
'--expose-gc'
]);

export default {
filterNodeArgumentsForWorkerThreads: argument => !processOnly.has(argument)
}
```
6 changes: 6 additions & 0 deletions lib/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -388,9 +388,15 @@ export default async function loadCli() { // eslint-disable-line complexity
exit(error.message);
}

const workerThreads = combined.workerThreads !== false;

let nodeArguments;
try {
nodeArguments = normalizeNodeArguments(conf.nodeArguments, argv['node-arguments']);
if (workerThreads && 'filterNodeArgumentsForWorkerThreads' in conf) {
const {filterNodeArgumentsForWorkerThreads: filter} = conf;
nodeArguments = nodeArguments.filter(argument => filter(argument));
}
} catch (error) {
exit(error.message);
}
Expand Down
7 changes: 7 additions & 0 deletions lib/load-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,13 @@ export async function loadConfig({configFile, resolveFrom = process.cwd(), defau
...defaults, nonSemVerExperiments: {}, ...fileConf, ...packageConf, projectDir, configFile,
};

if (
'filterNodeArgumentsForWorkerThreads' in config
&& typeof config.filterNodeArgumentsForWorkerThreads !== 'function'
) {
throw new Error(`filterNodeArgumentsForWorkerThreads from ${fileForErrorMessage} must be a function`);
}

const {nonSemVerExperiments: experiments} = config;
if (!isPlainObject(experiments)) {
throw new Error(`nonSemVerExperiments from ${fileForErrorMessage} must be an object`);
Expand Down
2 changes: 2 additions & 0 deletions test-tap/api.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import fs from 'node:fs';
import path from 'node:path';
import process from 'node:process';
import {fileURLToPath} from 'node:url';

import ciInfo from 'ci-info';
Expand All @@ -20,6 +21,7 @@ async function apiCreator(options = {}) {
options.globs = normalizeGlobs({
files: options.files, ignoredByWatcher: options.watchMode?.ignoreChanges, extensions: options.extensions, providers: [],
});
options.nodeArguments = process.execArgv;
const instance = new Api(options);

return instance;
Expand Down
3 changes: 3 additions & 0 deletions test-tap/fixture/load-config/non-function/ava.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default {
filterNodeArgumentsForWorkerThreads: [],
};
3 changes: 3 additions & 0 deletions test-tap/fixture/load-config/non-function/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"type": "module"
}
2 changes: 2 additions & 0 deletions test/config/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ test.serial('throws an error if a .js config file has no default export', notOk(

test.serial('throws an error if a config file contains `ava` property', notOk('contains-ava-property'));

test.serial('throws an error if a config file contains a non-function `filterNodeArgumentsForWorkerThreads` property', notOk('non-function'));

test.serial('throws an error if a config file contains a non-object `nonSemVerExperiments` property', notOk('non-object-experiments'));

test.serial('throws an error if a config file enables an unsupported experiment', notOk('unsupported-experiments'));
Expand Down
12 changes: 12 additions & 0 deletions test/config/snapshots/loader.js.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ Generated by [AVA](https://avajs.dev).

'Encountered ’ava’ property in ava.config.js; avoid wrapping the configuration'

## throws an error if a config file contains a non-function `filterNodeArgumentsForWorkerThreads` property

> error message

'filterNodeArgumentsForWorkerThreads from ava.config.js must be a function'

## throws an error if a config file contains a non-object `nonSemVerExperiments` property

> error message
Expand All @@ -57,3 +63,9 @@ Generated by [AVA](https://avajs.dev).
> error message

'Conflicting configuration in ava.config.js and ava.config.cjs'

## throws an error if a config file contains a non-function `threadArgumentsFilter` property

> error message

'threadArgumentsFilter from ava.config.js must be a function'
Binary file modified test/config/snapshots/loader.js.snap
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"type": "module"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import test from 'ava';

test('exec arguments unfiltered', t => {
t.plan(2);
t.truthy(process.execArgv.includes('--throw-deprecation'));
t.truthy(process.execArgv.includes('--allow-natives-syntax'));
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const processOnly = new Set(['--allow-natives-syntax']);

export default {
nodeArguments: [
'--throw-deprecation',
'--allow-natives-syntax',
],
filterNodeArgumentsForWorkerThreads: argument => !processOnly.has(argument),
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import test from 'ava';

test('exec arguments filtered', t => {
t.plan(2);
t.truthy(process.execArgv.includes('--throw-deprecation'));
t.falsy(process.execArgv.includes('--allow-natives-syntax'));
});
44 changes: 44 additions & 0 deletions test/node-arguments/snapshots/test.js.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,28 @@ Generated by [AVA](https://avajs.dev).
},
]

## `filterNodeArgumentsForWorkerThreads` configuration filters arguments for worker thread

> tests pass

[
{
file: 'thread.js',
title: 'exec arguments filtered',
},
]

## `filterNodeArgumentsForWorkerThreads` configuration ignored for worker process

> tests pass

[
{
file: 'process.js',
title: 'exec arguments unfiltered',
},
]

## detects incomplete --node-arguments

> fails with message
Expand All @@ -31,3 +53,25 @@ Generated by [AVA](https://avajs.dev).
title: 'works',
},
]

## `threadArgumentsFilter` configuration filters arguments for worker thread

> tests pass

[
{
file: 'thread.js',
title: 'exec arguments filtered',
},
]

## `threadArgumentsFilter` configuration ignored for worker process

> tests pass

[
{
file: 'process.js',
title: 'exec arguments unfiltered',
},
]
Binary file modified test/node-arguments/snapshots/test.js.snap
Binary file not shown.
20 changes: 20 additions & 0 deletions test/node-arguments/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,26 @@ test('passed node arguments to workers', async t => {
t.snapshot(result.stats.passed, 'tests pass');
});

test('`filterNodeArgumentsForWorkerThreads` configuration filters arguments for worker thread', async t => {
const options = {
cwd: cwd('thread-arguments-filter'),
};

const result = await fixture(['--config=thread-arguments-filter.config.mjs', 'thread.js'], options);

t.snapshot(result.stats.passed, 'tests pass');
});

test('`filterNodeArgumentsForWorkerThreads` configuration ignored for worker process', async t => {
const options = {
cwd: cwd('thread-arguments-filter'),
};

const result = await fixture(['--config=thread-arguments-filter.config.mjs', '--no-worker-threads', 'process.js'], options);

t.snapshot(result.stats.passed, 'tests pass');
});

test('detects incomplete --node-arguments', async t => {
const options = {
cwd: cwd('node-arguments'),
Expand Down