From cd0d9ddb7c6b0e4d9080784635359126f6176a05 Mon Sep 17 00:00:00 2001 From: Lenvin Gonsalves <41874033+98lenvi@users.noreply.github.com> Date: Mon, 25 Jul 2022 03:11:46 +0530 Subject: [PATCH] test_runner: add support for boolean values for `concurrency` option PR-URL: https://github.com/nodejs/node/pull/43887 Fixes: https://github.com/nodejs/node/issues/43837 Reviewed-By: Antoine du Hamel Reviewed-By: Jacob Smith --- doc/api/test.md | 9 ++++++-- lib/internal/test_runner/test.js | 10 ++++++-- test/parallel/test-runner-concurrency.js | 29 ++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 4 deletions(-) create mode 100644 test/parallel/test-runner-concurrency.js diff --git a/doc/api/test.md b/doc/api/test.md index 2708324b1dc253..c7a042b5efe19c 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); + }); + } +);