Skip to content

Commit

Permalink
Use same default seed for method and class ordering
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
marcphilipp committed May 17, 2024
1 parent 2a777a8 commit 3afec1d
Show file tree
Hide file tree
Showing 4 changed files with 24 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ repository on GitHub.
[[release-notes-5.11.0-M2-junit-jupiter-bug-fixes]]
==== Bug Fixes

* ❓
* `MethodOrderer.Random` and `ClassOrderer.Random` now use the same default seed that is
generated during class initialization.

[[release-notes-5.11.0-M2-junit-jupiter-deprecations-and-breaking-changes]]
==== Deprecations and Breaking Changes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,8 +185,8 @@ private static int getOrder(ClassDescriptor descriptor) {
* <h2>Custom Seed</h2>
*
* <p>By default, the random <em>seed</em> used for ordering classes is the
* value returned by {@link System#nanoTime()} during static initialization
* of this class. In order to support repeatable builds, the value of the
* value returned by {@link System#nanoTime()} during static class
* initialization. In order to support repeatable builds, the value of the
* default random seed is logged at {@code CONFIG} level. In addition, a
* custom seed (potentially the default seed from the previous test plan
* execution) may be specified via the {@value Random#RANDOM_SEED_PROPERTY_NAME}
Expand All @@ -203,13 +203,12 @@ class Random implements ClassOrderer {
private static final Logger logger = LoggerFactory.getLogger(Random.class);

/**
* Default seed, which is generated during initialization of this class
* via {@link System#nanoTime()} for reproducibility of tests.
* Default seed, which is generated during initialization of the
* {@link MethodOrderer.Random} class for reproducibility of tests.
*/
private static final long DEFAULT_SEED;
static final long DEFAULT_SEED = MethodOrderer.Random.DEFAULT_SEED;

static {
DEFAULT_SEED = System.nanoTime();
logger.config(() -> "ClassOrderer.Random default seed: " + DEFAULT_SEED);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ class Random implements MethodOrderer {
* Default seed, which is generated during initialization of this class
* via {@link System#nanoTime()} for reproducibility of tests.
*/
private static final long DEFAULT_SEED;
static final long DEFAULT_SEED;

static {
DEFAULT_SEED = System.nanoTime();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,20 @@
* https://www.eclipse.org/legal/epl-v20.html
*/

package org.junit.jupiter.engine.extension;
package org.junit.jupiter.api;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.engine.Constants.DEFAULT_TEST_CLASS_ORDER_PROPERTY_NAME;
import static org.junit.jupiter.engine.Constants.DEFAULT_TEST_METHOD_ORDER_PROPERTY_NAME;
import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;

import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.OptionalLong;
import java.util.Set;
import java.util.stream.IntStream;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.ClassOrderer;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;
import org.junit.platform.testkit.engine.EngineTestKit;
import org.junit.platform.testkit.engine.Events;

Expand All @@ -39,21 +36,29 @@ class RandomlyOrderedTests {
void randomSeedForClassAndMethodOrderingIsDeterministic() {
IntStream.range(0, 20).forEach(i -> {
callSequence.clear();
var tests = executeTests(1618034L);
var tests = executeTests(OptionalLong.of(1618034));

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

private Events executeTests(long randomSeed) {
@Test
void defaultSeedForClassAndMethodOrderingIsIdentical() {
executeTests(OptionalLong.empty());

assertEquals(MethodOrderer.Random.DEFAULT_SEED, ClassOrderer.Random.DEFAULT_SEED);
}

private Events executeTests(@SuppressWarnings("OptionalUsedAsFieldOrParameterType") OptionalLong randomSeed) {
// @formatter:off
return EngineTestKit
EngineTestKit.Builder builder = EngineTestKit
.engine("junit-jupiter")
.configurationParameter(DEFAULT_TEST_CLASS_ORDER_PROPERTY_NAME, ClassOrderer.Random.class.getName())
.configurationParameter(DEFAULT_TEST_METHOD_ORDER_PROPERTY_NAME, MethodOrderer.Random.class.getName())
.configurationParameter(MethodOrderer.Random.RANDOM_SEED_PROPERTY_NAME, String.valueOf(randomSeed))
.configurationParameter(DEFAULT_TEST_METHOD_ORDER_PROPERTY_NAME, MethodOrderer.Random.class.getName());
randomSeed.ifPresent(seed -> builder.configurationParameter(MethodOrderer.Random.RANDOM_SEED_PROPERTY_NAME, String.valueOf(seed)));
return builder
.selectors(selectClass(A_TestCase.class), selectClass(B_TestCase.class), selectClass(C_TestCase.class))
.execute()
.testEvents();
Expand Down

0 comments on commit 3afec1d

Please sign in to comment.