diff --git a/doc/api/test.md b/doc/api/test.md index b48a56da3ecb27..66c26931cfb0a2 100644 --- a/doc/api/test.md +++ b/doc/api/test.md @@ -331,9 +331,14 @@ changes: does not have a name. * `options` {Object} Configuration options for the test. The following properties are supported: - * `concurrency` {number} The number of tests that can be run at the same time. + * `concurrency` {number|boolean} If a number is provided, + then that many tests would run in parallel. + If truthy, it would run (number of cpu cores - 1) + tests in parallel. + For subtests, it will be `Infinity` tests in parallel. + If falsy, it would only run one test at a time. If unspecified, subtests inherit this value from their parent. - **Default:** `1`. + **Default:** `false`. * `only` {boolean} If truthy, and the test context is configured to run `only` tests, then this test will be run. Otherwise, the test is skipped. **Default:** `false`. diff --git a/lib/internal/test_runner/test.js b/lib/internal/test_runner/test.js index fadba74d72da6e..1663958f9b7c94 100644 --- a/lib/internal/test_runner/test.js +++ b/lib/internal/test_runner/test.js @@ -4,6 +4,7 @@ const { ArrayPrototypeShift, ArrayPrototypeUnshift, FunctionPrototype, + MathMax, Number, PromisePrototypeThen, PromiseResolve, @@ -52,8 +53,7 @@ const noop = FunctionPrototype; const isTestRunner = getOptionValue('--test'); const testOnlyFlag = !isTestRunner && getOptionValue('--test-only'); // TODO(cjihrig): Use uv_available_parallelism() once it lands. -const rootConcurrency = isTestRunner ? cpus().length : 1; - +const rootConcurrency = isTestRunner ? MathMax(cpus().length - 1, 1) : 1; const kShouldAbort = Symbol('kShouldAbort'); @@ -151,6 +151,12 @@ class Test extends AsyncResource { if (isUint32(concurrency) && concurrency !== 0) { this.concurrency = concurrency; + } else if (typeof concurrency === 'boolean') { + if (concurrency) { + this.concurrency = isTestRunner ? MathMax(cpus().length - 1, 1) : Infinity; + } else { + this.concurrency = 1; + } } if (timeout != null && timeout !== Infinity) { diff --git a/test/parallel/test-runner-concurrency.js b/test/parallel/test-runner-concurrency.js new file mode 100644 index 00000000000000..802cff3e9be375 --- /dev/null +++ b/test/parallel/test-runner-concurrency.js @@ -0,0 +1,29 @@ +'use strict'; +require('../common'); +const { describe, it } = require('node:test'); +const assert = require('assert'); + +describe('Concurrency option (boolean) = true ', { concurrency: true }, () => { + let isFirstTestOver = false; + it('should start the first test', () => new Promise((resolve) => { + setImmediate(() => { isFirstTestOver = true; resolve(); }); + })); + it('should start before the previous test ends', () => { + // Should work even on single core CPUs + assert.strictEqual(isFirstTestOver, false); + }); +}); + +describe( + 'Concurrency option (boolean) = false ', + { concurrency: false }, + () => { + let isFirstTestOver = false; + it('should start the first test', () => new Promise((resolve) => { + setImmediate(() => { isFirstTestOver = true; resolve(); }); + })); + it('should start after the previous test ends', () => { + assert.strictEqual(isFirstTestOver, true); + }); + } +);