Skip to content

Commit f090637

Browse files
authored
test_runner: consolidate option parsing
This commit consolidates all option parsing for the test runner in the parseCommandLine() internal helper function. The exception is a couple of temporary flags used for feature gating which will eventually become no-ops. This consolidation is prep work for supporting running test files in the test runner process. PR-URL: #53849 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Moshe Atlow <moshe@atlow.co.il> Reviewed-By: Chemi Atlow <chemi@atlow.co.il>
1 parent ac75b2e commit f090637

File tree

3 files changed

+68
-55
lines changed

3 files changed

+68
-55
lines changed

lib/internal/main/test_runner.js

+14-44
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,31 @@
11
'use strict';
22

3-
const {
4-
NumberParseInt,
5-
RegExpPrototypeExec,
6-
StringPrototypeSplit,
7-
} = primordials;
8-
93
const {
104
prepareMainThreadExecution,
115
markBootstrapComplete,
126
} = require('internal/process/pre_execution');
13-
const { getOptionValue } = require('internal/options');
147
const { isUsingInspector } = require('internal/util/inspector');
158
const { run } = require('internal/test_runner/runner');
16-
const { setupTestReporters } = require('internal/test_runner/utils');
17-
const { exitCodes: { kGenericUserError } } = internalBinding('errors');
189
const {
19-
codes: {
20-
ERR_INVALID_ARG_VALUE,
21-
},
22-
} = require('internal/errors');
23-
10+
parseCommandLine,
11+
setupTestReporters,
12+
} = require('internal/test_runner/utils');
13+
const { exitCodes: { kGenericUserError } } = internalBinding('errors');
2414
let debug = require('internal/util/debuglog').debuglog('test_runner', (fn) => {
2515
debug = fn;
2616
});
2717

2818
prepareMainThreadExecution(false);
2919
markBootstrapComplete();
3020

31-
let concurrency = getOptionValue('--test-concurrency') || true;
21+
const {
22+
perFileTimeout,
23+
runnerConcurrency,
24+
shard,
25+
watchMode,
26+
} = parseCommandLine();
27+
28+
let concurrency = runnerConcurrency;
3229
let inspectPort;
3330

3431
if (isUsingInspector()) {
@@ -38,39 +35,12 @@ if (isUsingInspector()) {
3835
inspectPort = process.debugPort;
3936
}
4037

41-
let shard;
42-
const shardOption = getOptionValue('--test-shard');
43-
if (shardOption) {
44-
if (!RegExpPrototypeExec(/^\d+\/\d+$/, shardOption)) {
45-
process.exitCode = kGenericUserError;
46-
47-
throw new ERR_INVALID_ARG_VALUE(
48-
'--test-shard',
49-
shardOption,
50-
'must be in the form of <index>/<total>',
51-
);
52-
}
53-
54-
const { 0: indexStr, 1: totalStr } = StringPrototypeSplit(shardOption, '/');
55-
56-
const index = NumberParseInt(indexStr, 10);
57-
const total = NumberParseInt(totalStr, 10);
58-
59-
shard = {
60-
__proto__: null,
61-
index,
62-
total,
63-
};
64-
}
65-
66-
const timeout = getOptionValue('--test-timeout') || Infinity;
67-
6838
const options = {
6939
concurrency,
7040
inspectPort,
71-
watch: getOptionValue('--watch'),
41+
watch: watchMode,
7242
setup: setupTestReporters,
73-
timeout,
43+
timeout: perFileTimeout,
7444
shard,
7545
};
7646
debug('test runner configuration:', options);

lib/internal/test_runner/coverage.js

+13-11
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,20 @@ const {
2525
readFileSync,
2626
} = require('fs');
2727
const { setupCoverageHooks } = require('internal/util');
28-
const { getOptionValue } = require('internal/options');
2928
const { tmpdir } = require('os');
3029
const { join, resolve, relative, matchesGlob } = require('path');
3130
const { fileURLToPath } = require('internal/url');
3231
const { kMappings, SourceMap } = require('internal/source_map/source_map');
32+
const { parseCommandLine } = require('internal/test_runner/utils');
3333
const kCoverageFileRegex = /^coverage-(\d+)-(\d{13})-(\d+)\.json$/;
3434
const kIgnoreRegex = /\/\* node:coverage ignore next (?<count>\d+ )?\*\//;
3535
const kLineEndingRegex = /\r?\n$/u;
3636
const kLineSplitRegex = /(?<=\r?\n)/u;
3737
const kStatusRegex = /\/\* node:coverage (?<status>enable|disable) \*\//;
38-
const excludeFileGlobs = getOptionValue('--test-coverage-exclude');
39-
const includeFileGlobs = getOptionValue('--test-coverage-include');
38+
const {
39+
coverageExcludeGlobs,
40+
coverageIncludeGlobs,
41+
} = parseCommandLine();
4042

4143
class CoverageLine {
4244
constructor(line, startOffset, src, length = src?.length) {
@@ -498,18 +500,18 @@ function shouldSkipFileCoverage(url, workingDirectory) {
498500
const relativePath = relative(workingDirectory, absolutePath);
499501

500502
// This check filters out files that match the exclude globs.
501-
if (excludeFileGlobs?.length > 0) {
502-
for (let i = 0; i < excludeFileGlobs.length; ++i) {
503-
if (matchesGlob(relativePath, excludeFileGlobs[i]) ||
504-
matchesGlob(absolutePath, excludeFileGlobs[i])) return true;
503+
if (coverageExcludeGlobs?.length > 0) {
504+
for (let i = 0; i < coverageExcludeGlobs.length; ++i) {
505+
if (matchesGlob(relativePath, coverageExcludeGlobs[i]) ||
506+
matchesGlob(absolutePath, coverageExcludeGlobs[i])) return true;
505507
}
506508
}
507509

508510
// This check filters out files that do not match the include globs.
509-
if (includeFileGlobs?.length > 0) {
510-
for (let i = 0; i < includeFileGlobs.length; ++i) {
511-
if (matchesGlob(relativePath, includeFileGlobs[i]) ||
512-
matchesGlob(absolutePath, includeFileGlobs[i])) return false;
511+
if (coverageIncludeGlobs?.length > 0) {
512+
for (let i = 0; i < coverageIncludeGlobs.length; ++i) {
513+
if (matchesGlob(relativePath, coverageIncludeGlobs[i]) ||
514+
matchesGlob(absolutePath, coverageIncludeGlobs[i])) return false;
513515
}
514516
return true;
515517
}

lib/internal/test_runner/utils.js

+41
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const {
99
MathFloor,
1010
MathMax,
1111
MathMin,
12+
NumberParseInt,
1213
NumberPrototypeToFixed,
1314
ObjectGetOwnPropertyDescriptor,
1415
RegExp,
@@ -19,6 +20,7 @@ const {
1920
StringPrototypePadStart,
2021
StringPrototypeRepeat,
2122
StringPrototypeSlice,
23+
StringPrototypeSplit,
2224
} = primordials;
2325

2426
const { AsyncResource } = require('async_hooks');
@@ -196,13 +198,19 @@ function parseCommandLine() {
196198
const forceExit = getOptionValue('--test-force-exit');
197199
const sourceMaps = getOptionValue('--enable-source-maps');
198200
const updateSnapshots = getOptionValue('--test-update-snapshots');
201+
const watchMode = getOptionValue('--watch');
199202
const isChildProcess = process.env.NODE_TEST_CONTEXT === 'child';
200203
const isChildProcessV8 = process.env.NODE_TEST_CONTEXT === 'child-v8';
204+
let coverageExcludeGlobs;
205+
let coverageIncludeGlobs;
201206
let destinations;
207+
let perFileTimeout;
202208
let reporters;
209+
let runnerConcurrency;
203210
let testNamePatterns;
204211
let testSkipPatterns;
205212
let testOnlyFlag;
213+
let shard;
206214

207215
if (isChildProcessV8) {
208216
kBuiltinReporters.set('v8-serializer', 'internal/test_runner/reporter/v8-serializer');
@@ -232,9 +240,31 @@ function parseCommandLine() {
232240
}
233241

234242
if (isTestRunner) {
243+
perFileTimeout = getOptionValue('--test-timeout') || Infinity;
244+
runnerConcurrency = getOptionValue('--test-concurrency') || true;
235245
testOnlyFlag = false;
236246
testNamePatterns = null;
247+
248+
const shardOption = getOptionValue('--test-shard');
249+
if (shardOption) {
250+
if (!RegExpPrototypeExec(/^\d+\/\d+$/, shardOption)) {
251+
throw new ERR_INVALID_ARG_VALUE(
252+
'--test-shard',
253+
shardOption,
254+
'must be in the form of <index>/<total>',
255+
);
256+
}
257+
258+
const indexAndTotal = StringPrototypeSplit(shardOption, '/');
259+
shard = {
260+
__proto__: null,
261+
index: NumberParseInt(indexAndTotal[0], 10),
262+
total: NumberParseInt(indexAndTotal[1], 10),
263+
};
264+
}
237265
} else {
266+
perFileTimeout = Infinity;
267+
runnerConcurrency = 1;
238268
const testNamePatternFlag = getOptionValue('--test-name-pattern');
239269
testOnlyFlag = getOptionValue('--test-only');
240270
testNamePatterns = testNamePatternFlag?.length > 0 ?
@@ -247,18 +277,29 @@ function parseCommandLine() {
247277
ArrayPrototypeMap(testSkipPatternFlag, (re) => convertStringToRegExp(re, '--test-skip-pattern')) : null;
248278
}
249279

280+
if (coverage) {
281+
coverageExcludeGlobs = getOptionValue('--test-coverage-exclude');
282+
coverageIncludeGlobs = getOptionValue('--test-coverage-include');
283+
}
284+
250285
globalTestOptions = {
251286
__proto__: null,
252287
isTestRunner,
253288
coverage,
289+
coverageExcludeGlobs,
290+
coverageIncludeGlobs,
254291
forceExit,
292+
perFileTimeout,
293+
runnerConcurrency,
294+
shard,
255295
sourceMaps,
256296
testOnlyFlag,
257297
testNamePatterns,
258298
testSkipPatterns,
259299
updateSnapshots,
260300
reporters,
261301
destinations,
302+
watchMode,
262303
};
263304

264305
return globalTestOptions;

0 commit comments

Comments
 (0)