diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/RandomOrdererUtils.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/RandomOrdererUtils.java index 22ae43b73ec6..f5bf57ff208a 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/RandomOrdererUtils.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/RandomOrdererUtils.java @@ -11,6 +11,8 @@ package org.junit.jupiter.api; import java.util.Optional; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; import java.util.function.Function; import org.junit.platform.commons.logging.Logger; @@ -26,8 +28,12 @@ class RandomOrdererUtils { static final String RANDOM_SEED_PROPERTY_NAME = "junit.jupiter.execution.order.random.seed"; + static final String RANDOM_SEED_LOG_FREQUENCY_PROPERTY_NAME = "junit.jupiter.execution.order.random.seed.log.frequency"; + static final long DEFAULT_SEED = System.nanoTime(); + static final Set CUSTOM_SEEDS_ALREADY_LOGGED = new CopyOnWriteArraySet<>(); + static Long getSeed(Function> configurationParameterLookup, Logger logger) { return getCustomSeed(configurationParameterLookup, logger).orElse(DEFAULT_SEED); } @@ -36,8 +42,16 @@ private static Optional getCustomSeed(Function> c Logger logger) { return configurationParameterLookup.apply(RANDOM_SEED_PROPERTY_NAME).map(configurationParameter -> { try { - logger.config(() -> "Using custom seed for configuration parameter [%s] with value [%s].".formatted( - RANDOM_SEED_PROPERTY_NAME, configurationParameter)); + // TODO Replace seed logging memory with launcher session store backed logic + var frequency = configurationParameterLookup.apply(RANDOM_SEED_LOG_FREQUENCY_PROPERTY_NAME).orElse( + "once_per_runtime"); + if (!CUSTOM_SEEDS_ALREADY_LOGGED.contains(configurationParameter)) { + if ("once_per_runtime".equalsIgnoreCase(frequency)) { + CUSTOM_SEEDS_ALREADY_LOGGED.add(configurationParameter); + } + logger.config(() -> "Using custom seed for configuration parameter [%s] with value [%s].".formatted( + RANDOM_SEED_PROPERTY_NAME, configurationParameter)); + } return Long.valueOf(configurationParameter); } catch (NumberFormatException ex) { diff --git a/jupiter-tests/src/test/java/org/junit/jupiter/api/RandomlyOrderedTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/RandomlyOrderedTests.java index 5f4e2185dbbd..4f59ee53dd01 100644 --- a/jupiter-tests/src/test/java/org/junit/jupiter/api/RandomlyOrderedTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/RandomlyOrderedTests.java @@ -18,8 +18,11 @@ import java.util.Collections; import java.util.LinkedHashSet; import java.util.Set; +import java.util.logging.LogRecord; import java.util.stream.IntStream; +import org.junit.jupiter.api.fixtures.TrackLogRecords; +import org.junit.platform.commons.logging.LogRecordListener; import org.junit.platform.testkit.engine.EngineTestKit; import org.junit.platform.testkit.engine.Events; @@ -31,24 +34,27 @@ class RandomlyOrderedTests { private static final Set callSequence = Collections.synchronizedSet(new LinkedHashSet<>()); @Test - void randomSeedForClassAndMethodOrderingIsDeterministic() { - IntStream.range(0, 20).forEach(i -> { + void randomSeedForClassAndMethodOrderingIsDeterministic(@TrackLogRecords LogRecordListener listener) { + var seed = "1618034"; + IntStream.range(0, 20).forEach(_ -> { callSequence.clear(); - var tests = executeTests(1618034); + var tests = executeTests(seed); 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"); }); + + assertThat(listener.stream().map(LogRecord::getMessage).filter(message -> message.contains(seed))).hasSize(1); // was logged 80 times before #4647 } - private Events executeTests(@SuppressWarnings("SameParameterValue") long randomSeed) { + private Events executeTests(@SuppressWarnings("SameParameterValue") String randomSeed) { // @formatter:off return 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(MethodOrderer.Random.RANDOM_SEED_PROPERTY_NAME, randomSeed) .selectors(selectClasses(A_TestCase.class, B_TestCase.class, C_TestCase.class)) .execute() .testEvents(); diff --git a/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/OrderedMethodTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/OrderedMethodTests.java index 5751c03494eb..5f98a1fad865 100644 --- a/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/OrderedMethodTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/OrderedMethodTests.java @@ -386,7 +386,8 @@ private Events executeRandomTestCaseInParallelWithRandomSeed(String seed) { var configurationParameters = Map.of(// PARALLEL_EXECUTION_ENABLED_PROPERTY_NAME, "true", // DEFAULT_EXECUTION_MODE_PROPERTY_NAME, "concurrent", // - RANDOM_SEED_PROPERTY_NAME, seed // + RANDOM_SEED_PROPERTY_NAME, seed, // + "junit.jupiter.execution.order.random.seed.log.frequency", "always" // ); // @formatter:off