@@ -7,11 +7,14 @@ const {
77 ArrayPrototypePop,
88 ArrayPrototypePush,
99 ArrayPrototypeReduce,
10+ ArrayPrototypeSlice,
1011 ArrayPrototypeSome,
1112 JSONParse,
1213 MathFloor,
14+ MathImul,
1315 MathMax,
1416 MathMin,
17+ MathRandom,
1518 NumberParseInt,
1619 NumberPrototypeToFixed,
1720 ObjectGetOwnPropertyDescriptor,
@@ -45,6 +48,7 @@ const { compose } = require('stream');
4548const {
4649 validateInteger,
4750 validateFunction,
51+ validateUint32,
4852} = require ( 'internal/validators' ) ;
4953const { validatePath } = require ( 'internal/fs/utils' ) ;
5054const { kEmptyObject } = require ( 'internal/util' ) ;
@@ -58,6 +62,7 @@ const coverageColors = {
5862
5963const kMultipleCallbackInvocations = 'multipleCallbackInvocations' ;
6064const kRegExpPattern = / ^ \/ ( .* ) \/ ( [ a - z ] * ) $ / ;
65+ const kMaxRandomSeed = 0xFFFF_FFFF ;
6166
6267const kPatterns = [ 'test' , 'test/**/*' , 'test-*' , '*[._-]test' ] ;
6368const kFileExtensions = [ 'js' , 'mjs' , 'cjs' ] ;
@@ -133,6 +138,54 @@ const kBuiltinReporters = new SafeMap([
133138const kDefaultReporter = 'spec' ;
134139const kDefaultDestination = 'stdout' ;
135140
141+ /**
142+ * Create a random uint32 seed.
143+ * @returns {number }
144+ */
145+ function createRandomSeed ( ) {
146+ return MathFloor ( MathRandom ( ) * ( kMaxRandomSeed + 1 ) ) ;
147+ }
148+
149+ /**
150+ * Create a Mulberry32 pseudo-random number generator from a uint32 seed.
151+ * @param {number } seed
152+ * @returns {() => number }
153+ */
154+ function createSeededGenerator ( seed ) {
155+ let state = seed >>> 0 ;
156+ return ( ) => {
157+ state = ( state + 0x6D2B79F5 ) | 0 ;
158+ let value = MathImul ( state ^ state >>> 15 , 1 | state ) ;
159+ value ^= value + MathImul ( value ^ value >>> 7 , 61 | value ) ;
160+ return ( ( value ^ value >>> 14 ) >>> 0 ) / 4_294_967_296 ;
161+ } ;
162+ }
163+
164+ /**
165+ * Return a deterministically shuffled copy of an array.
166+ * @template T
167+ * @param {T[] } values
168+ * @param {number } seed
169+ * @returns {T[] }
170+ */
171+ function shuffleArrayWithSeed ( values , seed ) {
172+ if ( values . length < 2 ) {
173+ return values ;
174+ }
175+
176+ const randomized = ArrayPrototypeSlice ( values ) ;
177+ const random = createSeededGenerator ( seed ) ;
178+
179+ for ( let i = randomized . length - 1 ; i > 0 ; i -- ) {
180+ const j = MathFloor ( random ( ) * ( i + 1 ) ) ;
181+ const tmp = randomized [ i ] ;
182+ randomized [ i ] = randomized [ j ] ;
183+ randomized [ j ] = tmp ;
184+ }
185+
186+ return randomized ;
187+ }
188+
136189function tryBuiltinReporter ( name ) {
137190 const builtinPath = kBuiltinReporters . get ( name ) ;
138191
@@ -217,6 +270,10 @@ function parseCommandLine() {
217270 const updateSnapshots = getOptionValue ( '--test-update-snapshots' ) ;
218271 const watch = getOptionValue ( '--watch' ) ;
219272 const timeout = getOptionValue ( '--test-timeout' ) || Infinity ;
273+ let randomize = getOptionValue ( '--test-randomize' ) ;
274+ const hasRandomSeedOption = getOptionValue ( '[has_test_random_seed]' ) ;
275+ const randomSeedOption = getOptionValue ( '--test-random-seed' ) ;
276+ let randomSeed ;
220277 const rerunFailuresFilePath = getOptionValue ( '--test-rerun-failures' ) ;
221278 const isChildProcess = process . env . NODE_TEST_CONTEXT === 'child' ;
222279 const isChildProcessV8 = process . env . NODE_TEST_CONTEXT === 'child-v8' ;
@@ -328,6 +385,12 @@ function parseCommandLine() {
328385 validatePath ( rerunFailuresFilePath , '--test-rerun-failures' ) ;
329386 }
330387
388+ if ( hasRandomSeedOption ) {
389+ validateUint32 ( randomSeedOption , '--test-random-seed' ) ;
390+ randomSeed = randomSeedOption ;
391+ randomize = true ;
392+ }
393+
331394 const setup = reporterScope . bind ( async ( rootReporter ) => {
332395 const reportersMap = await getReportersMap ( reporters , destinations ) ;
333396 for ( let i = 0 ; i < reportersMap . length ; i ++ ) {
@@ -362,6 +425,8 @@ function parseCommandLine() {
362425 timeout,
363426 updateSnapshots,
364427 watch,
428+ randomize,
429+ randomSeed,
365430 rerunFailuresFilePath,
366431 } ;
367432
@@ -649,10 +714,14 @@ async function setupGlobalSetupTeardownFunctions(globalSetupPath, cwd) {
649714module . exports = {
650715 convertStringToRegExp,
651716 countCompletedTest,
717+ createRandomSeed,
718+ createSeededGenerator,
652719 createDeferredCallback,
653720 isTestFailureError,
654721 kDefaultPattern,
722+ kMaxRandomSeed,
655723 parseCommandLine,
724+ shuffleArrayWithSeed,
656725 reporterScope,
657726 shouldColorizeTestFiles,
658727 getCoverageReport,
0 commit comments