Skip to content

Commit 6c663b1

Browse files
committed
Use same default seed for method and class ordering (#3821)
Prior to this commit the default seeds were generated separately but the configuration parameter that allows using a fixed seed only allowed to set both to the same value making it impossible to reproduce a failure for different default seeds. Since the default seed is now identical, this scenario is avoided. Fixes #3817. (cherry picked from commit a422c5a)
1 parent d29e3eb commit 6c663b1

File tree

5 files changed

+74
-75
lines changed

5 files changed

+74
-75
lines changed

documentation/src/docs/asciidoc/release-notes/release-notes-5.10.3.adoc

+2
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ on GitHub.
3939

4040
* `TempDir` suppresses `NoSuchFileException` when deleting files that may have been deleted
4141
by another thread or process.
42+
* `MethodOrderer.Random` and `ClassOrderer.Random` now use the same default seed that is
43+
generated during class initialization.
4244

4345
==== Deprecations and Breaking Changes
4446

junit-jupiter-api/src/main/java/org/junit/jupiter/api/ClassOrderer.java

+5-33
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515

1616
import java.util.Collections;
1717
import java.util.Comparator;
18-
import java.util.Optional;
1918

2019
import org.apiguardian.api.API;
2120
import org.junit.platform.commons.logging.Logger;
@@ -185,8 +184,8 @@ private static int getOrder(ClassDescriptor descriptor) {
185184
* <h2>Custom Seed</h2>
186185
*
187186
* <p>By default, the random <em>seed</em> used for ordering classes is the
188-
* value returned by {@link System#nanoTime()} during static initialization
189-
* of this class. In order to support repeatable builds, the value of the
187+
* value returned by {@link System#nanoTime()} during static class
188+
* initialization. In order to support repeatable builds, the value of the
190189
* default random seed is logged at {@code CONFIG} level. In addition, a
191190
* custom seed (potentially the default seed from the previous test plan
192191
* execution) may be specified via the {@value Random#RANDOM_SEED_PROPERTY_NAME}
@@ -202,15 +201,8 @@ class Random implements ClassOrderer {
202201

203202
private static final Logger logger = LoggerFactory.getLogger(Random.class);
204203

205-
/**
206-
* Default seed, which is generated during initialization of this class
207-
* via {@link System#nanoTime()} for reproducibility of tests.
208-
*/
209-
private static final long DEFAULT_SEED;
210-
211204
static {
212-
DEFAULT_SEED = System.nanoTime();
213-
logger.config(() -> "ClassOrderer.Random default seed: " + DEFAULT_SEED);
205+
logger.config(() -> "ClassOrderer.Random default seed: " + RandomOrdererUtils.DEFAULT_SEED);
214206
}
215207

216208
/**
@@ -231,7 +223,7 @@ class Random implements ClassOrderer {
231223
*
232224
* @see MethodOrderer.Random
233225
*/
234-
public static final String RANDOM_SEED_PROPERTY_NAME = MethodOrderer.Random.RANDOM_SEED_PROPERTY_NAME;
226+
public static final String RANDOM_SEED_PROPERTY_NAME = RandomOrdererUtils.RANDOM_SEED_PROPERTY_NAME;
235227

236228
public Random() {
237229
}
@@ -243,27 +235,7 @@ public Random() {
243235
@Override
244236
public void orderClasses(ClassOrdererContext context) {
245237
Collections.shuffle(context.getClassDescriptors(),
246-
new java.util.Random(getCustomSeed(context).orElse(DEFAULT_SEED)));
247-
}
248-
249-
private Optional<Long> getCustomSeed(ClassOrdererContext context) {
250-
return context.getConfigurationParameter(RANDOM_SEED_PROPERTY_NAME).map(configurationParameter -> {
251-
Long seed = null;
252-
try {
253-
seed = Long.valueOf(configurationParameter);
254-
logger.config(
255-
() -> String.format("Using custom seed for configuration parameter [%s] with value [%s].",
256-
RANDOM_SEED_PROPERTY_NAME, configurationParameter));
257-
}
258-
catch (NumberFormatException ex) {
259-
logger.warn(ex,
260-
() -> String.format(
261-
"Failed to convert configuration parameter [%s] with value [%s] to a long. "
262-
+ "Using default seed [%s] as fallback.",
263-
RANDOM_SEED_PROPERTY_NAME, configurationParameter, DEFAULT_SEED));
264-
}
265-
return seed;
266-
});
238+
new java.util.Random(RandomOrdererUtils.getSeed(context::getConfigurationParameter, logger)));
267239
}
268240
}
269241

junit-jupiter-api/src/main/java/org/junit/jupiter/api/MethodOrderer.java

+6-32
Original file line numberDiff line numberDiff line change
@@ -248,11 +248,11 @@ private static int getOrder(MethodDescriptor descriptor) {
248248
* <h2>Custom Seed</h2>
249249
*
250250
* <p>By default, the random <em>seed</em> used for ordering methods is the
251-
* value returned by {@link System#nanoTime()} during static initialization
252-
* of this class. In order to support repeatable builds, the value of the
251+
* value returned by {@link System#nanoTime()} during static class
252+
* initialization. In order to support repeatable builds, the value of the
253253
* default random seed is logged at {@code CONFIG} level. In addition, a
254254
* custom seed (potentially the default seed from the previous test plan
255-
* execution) may be specified via the {@value ClassOrderer.Random#RANDOM_SEED_PROPERTY_NAME}
255+
* execution) may be specified via the {@value Random#RANDOM_SEED_PROPERTY_NAME}
256256
* <em>configuration parameter</em> which can be supplied via the {@code Launcher}
257257
* API, build tools (e.g., Gradle and Maven), a JVM system property, or the JUnit
258258
* Platform configuration file (i.e., a file named {@code junit-platform.properties}
@@ -265,15 +265,8 @@ class Random implements MethodOrderer {
265265

266266
private static final Logger logger = LoggerFactory.getLogger(Random.class);
267267

268-
/**
269-
* Default seed, which is generated during initialization of this class
270-
* via {@link System#nanoTime()} for reproducibility of tests.
271-
*/
272-
private static final long DEFAULT_SEED;
273-
274268
static {
275-
DEFAULT_SEED = System.nanoTime();
276-
logger.config(() -> "MethodOrderer.Random default seed: " + DEFAULT_SEED);
269+
logger.config(() -> "MethodOrderer.Random default seed: " + RandomOrdererUtils.DEFAULT_SEED);
277270
}
278271

279272
/**
@@ -294,7 +287,7 @@ class Random implements MethodOrderer {
294287
*
295288
* @see ClassOrderer.Random
296289
*/
297-
public static final String RANDOM_SEED_PROPERTY_NAME = "junit.jupiter.execution.order.random.seed";
290+
public static final String RANDOM_SEED_PROPERTY_NAME = RandomOrdererUtils.RANDOM_SEED_PROPERTY_NAME;
298291

299292
public Random() {
300293
}
@@ -306,28 +299,9 @@ public Random() {
306299
@Override
307300
public void orderMethods(MethodOrdererContext context) {
308301
Collections.shuffle(context.getMethodDescriptors(),
309-
new java.util.Random(getCustomSeed(context).orElse(DEFAULT_SEED)));
302+
new java.util.Random(RandomOrdererUtils.getSeed(context::getConfigurationParameter, logger)));
310303
}
311304

312-
private Optional<Long> getCustomSeed(MethodOrdererContext context) {
313-
return context.getConfigurationParameter(RANDOM_SEED_PROPERTY_NAME).map(configurationParameter -> {
314-
Long seed = null;
315-
try {
316-
seed = Long.valueOf(configurationParameter);
317-
logger.config(
318-
() -> String.format("Using custom seed for configuration parameter [%s] with value [%s].",
319-
RANDOM_SEED_PROPERTY_NAME, configurationParameter));
320-
}
321-
catch (NumberFormatException ex) {
322-
logger.warn(ex,
323-
() -> String.format(
324-
"Failed to convert configuration parameter [%s] with value [%s] to a long. "
325-
+ "Using default seed [%s] as fallback.",
326-
RANDOM_SEED_PROPERTY_NAME, configurationParameter, DEFAULT_SEED));
327-
}
328-
return seed;
329-
});
330-
}
331305
}
332306

333307
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright 2015-2024 the original author or authors.
3+
*
4+
* All rights reserved. This program and the accompanying materials are
5+
* made available under the terms of the Eclipse Public License v2.0 which
6+
* accompanies this distribution and is available at
7+
*
8+
* https://www.eclipse.org/legal/epl-v20.html
9+
*/
10+
11+
package org.junit.jupiter.api;
12+
13+
import java.util.Optional;
14+
import java.util.function.Function;
15+
16+
import org.junit.platform.commons.logging.Logger;
17+
18+
/**
19+
* Shared utility methods for ordering test classes and test methods randomly.
20+
*
21+
* @since 5.11
22+
* @see ClassOrderer.Random
23+
* @see MethodOrderer.Random
24+
*/
25+
class RandomOrdererUtils {
26+
27+
static final String RANDOM_SEED_PROPERTY_NAME = "junit.jupiter.execution.order.random.seed";
28+
29+
static final long DEFAULT_SEED = System.nanoTime();
30+
31+
static Long getSeed(Function<String, Optional<String>> configurationParameterLookup, Logger logger) {
32+
return getCustomSeed(configurationParameterLookup, logger).orElse(DEFAULT_SEED);
33+
}
34+
35+
private static Optional<Long> getCustomSeed(Function<String, Optional<String>> configurationParameterLookup,
36+
Logger logger) {
37+
return configurationParameterLookup.apply(RANDOM_SEED_PROPERTY_NAME).map(configurationParameter -> {
38+
try {
39+
logger.config(() -> String.format("Using custom seed for configuration parameter [%s] with value [%s].",
40+
RANDOM_SEED_PROPERTY_NAME, configurationParameter));
41+
return Long.valueOf(configurationParameter);
42+
}
43+
catch (NumberFormatException ex) {
44+
logger.warn(ex,
45+
() -> String.format(
46+
"Failed to convert configuration parameter [%s] with value [%s] to a long. "
47+
+ "Using default seed [%s] as fallback.",
48+
RANDOM_SEED_PROPERTY_NAME, configurationParameter, DEFAULT_SEED));
49+
return null;
50+
}
51+
});
52+
}
53+
}

junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/extension/RandomlyOrderedTests.java junit-jupiter-engine/src/test/java/org/junit/jupiter/api/RandomlyOrderedTests.java

+8-10
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
* https://www.eclipse.org/legal/epl-v20.html
99
*/
1010

11-
package org.junit.jupiter.engine.extension;
11+
package org.junit.jupiter.api;
1212

1313
import static org.assertj.core.api.Assertions.assertThat;
1414
import static org.junit.jupiter.engine.Constants.DEFAULT_TEST_CLASS_ORDER_PROPERTY_NAME;
@@ -20,11 +20,6 @@
2020
import java.util.Set;
2121
import java.util.stream.IntStream;
2222

23-
import org.junit.jupiter.api.BeforeEach;
24-
import org.junit.jupiter.api.ClassOrderer;
25-
import org.junit.jupiter.api.MethodOrderer;
26-
import org.junit.jupiter.api.Test;
27-
import org.junit.jupiter.api.TestInfo;
2823
import org.junit.platform.testkit.engine.EngineTestKit;
2924
import org.junit.platform.testkit.engine.Events;
3025

@@ -39,15 +34,15 @@ class RandomlyOrderedTests {
3934
void randomSeedForClassAndMethodOrderingIsDeterministic() {
4035
IntStream.range(0, 20).forEach(i -> {
4136
callSequence.clear();
42-
var tests = executeTests(1618034L);
37+
var tests = executeTests(1618034);
4338

4439
tests.assertStatistics(stats -> stats.succeeded(callSequence.size()));
4540
assertThat(callSequence).containsExactlyInAnyOrder("B_TestCase#b", "B_TestCase#c", "B_TestCase#a",
4641
"C_TestCase#b", "C_TestCase#c", "C_TestCase#a", "A_TestCase#b", "A_TestCase#c", "A_TestCase#a");
4742
});
4843
}
4944

50-
private Events executeTests(long randomSeed) {
45+
private Events executeTests(@SuppressWarnings("SameParameterValue") long randomSeed) {
5146
// @formatter:off
5247
return EngineTestKit
5348
.engine("junit-jupiter")
@@ -64,8 +59,8 @@ abstract static class BaseTestCase {
6459

6560
@BeforeEach
6661
void trackInvocations(TestInfo testInfo) {
67-
var testClass = testInfo.getTestClass().get();
68-
var testMethod = testInfo.getTestMethod().get();
62+
var testClass = testInfo.getTestClass().orElseThrow();
63+
var testMethod = testInfo.getTestMethod().orElseThrow();
6964

7065
callSequence.add(testClass.getSimpleName() + "#" + testMethod.getName());
7166
}
@@ -83,12 +78,15 @@ void c() {
8378
}
8479
}
8580

81+
@SuppressWarnings("NewClassNamingConvention")
8682
static class A_TestCase extends BaseTestCase {
8783
}
8884

85+
@SuppressWarnings("NewClassNamingConvention")
8986
static class B_TestCase extends BaseTestCase {
9087
}
9188

89+
@SuppressWarnings("NewClassNamingConvention")
9290
static class C_TestCase extends BaseTestCase {
9391
}
9492

0 commit comments

Comments
 (0)