| 'use strict'; |
| |
| require('../common'); |
| const assert = require('node:assert'); |
| const { spawnSync } = require('node:child_process'); |
| const { join } = require('node:path'); |
| const { describe, it } = require('node:test'); |
| const fixtures = require('../common/fixtures'); |
| |
| const testFixtures = fixtures.path('test-runner'); |
| const internalOrderFixture = fixtures.path('test-runner', 'randomize', 'internal-order.cjs'); |
| const rerunStateFile = fixtures.path('test-runner', 'rerun-state.json'); |
| const kShardFiles = ['a.cjs', 'b.cjs', 'c.cjs', 'd.cjs', 'e.cjs', 'f.cjs', 'g.cjs', 'h.cjs', 'i.cjs', 'j.cjs']; |
| const kInternalTests = ['a', 'b', 'c', 'd', 'e']; |
| |
| function getShardOrder(stdout) { |
| return Array.from(stdout.matchAll(/ok \d+ - ([a-j]\.cjs) this should pass/g), ({ 1: name }) => name); |
| } |
| |
| function getInternalExecutionOrder(stdout) { |
| const match = stdout.match(/EXECUTION_ORDER:([a-e](?:,[a-e])*)/); |
| assert(match, `Missing EXECUTION_ORDER marker in output: ${stdout}`); |
| return match[1].split(','); |
| } |
| |
| describe('test runner randomize flags via command line', () => { |
| it('should be deterministic with --test-random-seed', () => { |
| const args = [ |
| '--test', |
| '--test-reporter=tap', |
| '--test-concurrency=1', |
| '--test-randomize', |
| '--test-random-seed=12345', |
| join(testFixtures, 'shards/*.cjs'), |
| ]; |
| const first = spawnSync(process.execPath, args); |
| const second = spawnSync(process.execPath, args); |
| |
| assert.strictEqual(first.stderr.toString(), ''); |
| assert.strictEqual(second.stderr.toString(), ''); |
| assert.strictEqual(first.status, 0); |
| assert.strictEqual(second.status, 0); |
| |
| const firstOrder = getShardOrder(first.stdout.toString()); |
| const secondOrder = getShardOrder(second.stdout.toString()); |
| assert.deepStrictEqual(firstOrder, secondOrder); |
| assert.deepStrictEqual([...firstOrder].sort(), kShardFiles); |
| }); |
| |
| it('should use different orders for different seeds', () => { |
| const first = spawnSync(process.execPath, [ |
| '--test', |
| '--test-reporter=tap', |
| '--test-concurrency=1', |
| '--test-randomize', |
| '--test-random-seed=11111', |
| join(testFixtures, 'shards/*.cjs'), |
| ]); |
| const second = spawnSync(process.execPath, [ |
| '--test', |
| '--test-reporter=tap', |
| '--test-concurrency=1', |
| '--test-randomize', |
| '--test-random-seed=22222', |
| join(testFixtures, 'shards/*.cjs'), |
| ]); |
| |
| assert.strictEqual(first.stderr.toString(), ''); |
| assert.strictEqual(second.stderr.toString(), ''); |
| assert.strictEqual(first.status, 0); |
| assert.strictEqual(second.status, 0); |
| |
| const firstOrder = getShardOrder(first.stdout.toString()); |
| const secondOrder = getShardOrder(second.stdout.toString()); |
| assert.notDeepStrictEqual(firstOrder, secondOrder); |
| assert.deepStrictEqual([...firstOrder].sort(), kShardFiles); |
| assert.deepStrictEqual([...secondOrder].sort(), kShardFiles); |
| }); |
| |
| it('should randomize deterministically with --test-random-seed alone', () => { |
| const args = [ |
| '--test', |
| '--test-reporter=tap', |
| '--test-concurrency=1', |
| '--test-random-seed=24680', |
| join(testFixtures, 'shards/*.cjs'), |
| ]; |
| const first = spawnSync(process.execPath, args); |
| const second = spawnSync(process.execPath, args); |
| |
| assert.strictEqual(first.stderr.toString(), ''); |
| assert.strictEqual(second.stderr.toString(), ''); |
| assert.strictEqual(first.status, 0); |
| assert.strictEqual(second.status, 0); |
| |
| const firstOrder = getShardOrder(first.stdout.toString()); |
| const secondOrder = getShardOrder(second.stdout.toString()); |
| assert.deepStrictEqual(firstOrder, secondOrder); |
| assert.deepStrictEqual([...firstOrder].sort(), kShardFiles); |
| assert.match(first.stdout.toString(), /# Randomized test order seed: 24680/); |
| }); |
| |
| it('should print the randomization seed when --test-randomize is used', () => { |
| const child = spawnSync(process.execPath, [ |
| '--test', |
| '--test-reporter=tap', |
| '--test-concurrency=1', |
| '--test-randomize', |
| join(testFixtures, 'shards/*.cjs'), |
| ]); |
| |
| assert.strictEqual(child.stderr.toString(), ''); |
| assert.strictEqual(child.status, 0); |
| assert.match(child.stdout.toString(), /# Randomized test order seed: \d+/); |
| }); |
| |
| it('should randomize internal test order deterministically with --test-random-seed', () => { |
| const args = [ |
| '--test', |
| '--test-reporter=tap', |
| '--test-random-seed=12345', |
| internalOrderFixture, |
| ]; |
| const first = spawnSync(process.execPath, args); |
| const second = spawnSync(process.execPath, args); |
| |
| assert.strictEqual(first.stderr.toString(), ''); |
| assert.strictEqual(second.stderr.toString(), ''); |
| assert.strictEqual(first.status, 0); |
| assert.strictEqual(second.status, 0); |
| |
| const firstOrder = getInternalExecutionOrder(first.stdout.toString()); |
| const secondOrder = getInternalExecutionOrder(second.stdout.toString()); |
| assert.deepStrictEqual(firstOrder, secondOrder); |
| assert.deepStrictEqual([...firstOrder].sort(), kInternalTests); |
| }); |
| |
| it('should randomize internal test order differently across seeds', () => { |
| const orders = []; |
| for (const seed of [11111, 22222, 33333, 44444]) { |
| const child = spawnSync(process.execPath, [ |
| '--test', |
| '--test-reporter=tap', |
| `--test-random-seed=${seed}`, |
| internalOrderFixture, |
| ]); |
| assert.strictEqual(child.stderr.toString(), ''); |
| assert.strictEqual(child.status, 0); |
| |
| const order = getInternalExecutionOrder(child.stdout.toString()); |
| assert.deepStrictEqual([...order].sort(), kInternalTests); |
| orders.push(order.join(',')); |
| } |
| |
| assert.notStrictEqual(new Set(orders).size, 1); |
| }); |
| |
| it('should reject --test-randomize with --watch', () => { |
| const child = spawnSync(process.execPath, [ |
| '--test', |
| '--watch', |
| '--test-randomize', |
| join(testFixtures, 'shards/*.cjs'), |
| ]); |
| |
| assert.strictEqual(child.stdout.toString(), ''); |
| assert.match(child.stderr.toString(), /The property 'options\.randomize' is not supported with watch mode\./); |
| assert.strictEqual(child.status, 1); |
| assert.strictEqual(child.signal, null); |
| }); |
| |
| it('should reject --test-random-seed with --watch', () => { |
| const child = spawnSync(process.execPath, [ |
| '--test', |
| '--watch', |
| '--test-random-seed=12345', |
| join(testFixtures, 'shards/*.cjs'), |
| ]); |
| |
| assert.strictEqual(child.stdout.toString(), ''); |
| assert.match(child.stderr.toString(), /The property 'options\.randomSeed' is not supported with watch mode\./); |
| assert.strictEqual(child.status, 1); |
| assert.strictEqual(child.signal, null); |
| }); |
| |
| it('should reject --test-randomize with --test-rerun-failures', () => { |
| const child = spawnSync(process.execPath, [ |
| '--test', |
| '--test-randomize', |
| '--test-rerun-failures', |
| rerunStateFile, |
| join(testFixtures, 'shards/*.cjs'), |
| ]); |
| |
| assert.strictEqual(child.stdout.toString(), ''); |
| assert.match(child.stderr.toString(), /The property 'options\.randomize' is not supported with rerun failures mode\./); |
| assert.strictEqual(child.status, 1); |
| assert.strictEqual(child.signal, null); |
| }); |
| |
| it('should reject --test-random-seed with --test-rerun-failures', () => { |
| const child = spawnSync(process.execPath, [ |
| '--test', |
| '--test-random-seed=12345', |
| '--test-rerun-failures', |
| rerunStateFile, |
| join(testFixtures, 'shards/*.cjs'), |
| ]); |
| |
| assert.strictEqual(child.stdout.toString(), ''); |
| assert.match(child.stderr.toString(), /The property 'options\.randomSeed' is not supported with rerun failures mode\./); |
| assert.strictEqual(child.status, 1); |
| assert.strictEqual(child.signal, null); |
| }); |
| |
| it('should reject out of range --test-random-seed values', () => { |
| const child = spawnSync(process.execPath, [ |
| '--test', |
| '--test-random-seed=4294967296', |
| join(testFixtures, 'shards/*.cjs'), |
| ]); |
| |
| assert.strictEqual(child.stdout.toString(), ''); |
| assert.match(child.stderr.toString(), /The value of "--test-random-seed" is out of range\. It must be >= 0 && <= 4294967295\. Received 4294967296/); |
| assert.strictEqual(child.status, 1); |
| assert.strictEqual(child.signal, null); |
| }); |
| }); |