diff --git a/CHANGELOG.md b/CHANGELOG.md
index fe83e8db1b..c60ef625b6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
+### Fixed
+[JUnit Platform Engine] Enable use of custom UUID generators ([#2887](https://github.com/cucumber/cucumber-jvm/pull/2926) M.P. Korstanje)
+[JUnit] Enable use of custom UUID generators ([#2887](https://github.com/cucumber/cucumber-jvm/pull/2926) M.P. Korstanje)
+[TestNG] Enable use of custom UUID generators ([#2887](https://github.com/cucumber/cucumber-jvm/pull/2926) M.P. Korstanje)
+
## [7.19.0] - 2024-09-19
### Changed
diff --git a/cucumber-core/README.md b/cucumber-core/README.md
index 050a49f067..3a973e6f27 100644
--- a/cucumber-core/README.md
+++ b/cucumber-core/README.md
@@ -53,7 +53,8 @@ cucumber.plugin= # comma separated plugin strings.
cucumber.object-factory= # object factory class name.
# example: com.example.MyObjectFactory
-cucumber.uuid-generator= # UUID generator class name.
+cucumber.uuid-generator # uuid generator class name of a registered service provider.
+ # default: io.cucumber.core.eventbus.RandomUuidGenerator
# example: com.example.MyUuidGenerator
cucumber.publish.enabled # true or false. default: false
@@ -89,12 +90,13 @@ Cucumber emits events on an event bus in many cases:
- during the feature file parsing
- when the test scenarios are executed
-An event has a UUID. The UUID generator can be configured using the `cucumber.uuid-generator` property:
+An event has a UUID. The UUID generator can be configured using the
+`cucumber.uuid-generator` property:
| UUID generator | Features | Performance [Millions UUID/second] | Typical usage example |
|-----------------------------------------------------|-----------------------------------------|------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| io.cucumber.core.eventbus.RandomUuidGenerator | Thread-safe, collision-free, multi-jvm | ~1 | Reports may be generated on different JVMs at the same time. A typical example would be one suite that tests against Firefox and another against Safari. The exact browser is configured through a property. These are then executed concurrently on different Gitlab runners. |
-| io.cucumber.core.eventbus.IncrementingUuidGenerator | Thread-safe, collision-free, single-jvm | ~130 | Reports are generated on a single JVM |
+| io.cucumber.core.eventbus.IncrementingUuidGenerator | Thread-safe, collision-free, single-jvm | ~130 | Reports are generated on a single JVM in a single execution of Cucumber. |
The performance gain on real projects depends on the feature size.
diff --git a/cucumber-core/src/main/java/io/cucumber/core/eventbus/IncrementingUuidGenerator.java b/cucumber-core/src/main/java/io/cucumber/core/eventbus/IncrementingUuidGenerator.java
index d403ece897..04bdd43644 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/eventbus/IncrementingUuidGenerator.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/eventbus/IncrementingUuidGenerator.java
@@ -10,20 +10,20 @@
* Thread-safe and collision-free UUID generator for single JVM. This is a
* sequence generator and each instance has its own counter. This generator is
* about 100 times faster than #RandomUuidGenerator.
- *
- * Properties:
- * - thread-safe
- * - collision-free in the same classloader
- * - almost collision-free in different classloaders / JVMs
- * - UUIDs generated using the instances from the same classloader are sortable
- *
- * UUID version 8 (custom) / variant 2 ...
- *
+ *
+ * Properties: - thread-safe - collision-free in the same classloader - almost
+ * collision-free in different classloaders / JVMs - UUIDs generated using the
+ * instances from the same classloader are sortable
+ *
+ * UUID
+ * version 8 (custom) / variant 2
+ *
+ *
* | 40 bits | 8 bits | 4 bits | 12 bits | 2 bits | 62 bits |
* | -------------------| -------------- | ------- | ------------- | ------- | ------- |
* | LSBs of epoch-time | sessionCounter | version | classloaderId | variant | counter |
- *
+ *
*/
public class IncrementingUuidGenerator implements UuidGenerator {
/**
@@ -84,7 +84,7 @@ public class IncrementingUuidGenerator implements UuidGenerator {
* classloaderId which produces about 1% collision rate on the
* classloaderId, and thus can have UUID collision if the epoch-time,
* session counter and counter have the same values).
- *
+ *
* @param classloaderId the new classloaderId (only the least significant 12
* bits are used)
* @see IncrementingUuidGenerator#classloaderId
diff --git a/cucumber-core/src/main/java/io/cucumber/core/runtime/UuidGeneratorServiceLoader.java b/cucumber-core/src/main/java/io/cucumber/core/runtime/UuidGeneratorServiceLoader.java
index d8fbb2baf2..30248fbf1d 100644
--- a/cucumber-core/src/main/java/io/cucumber/core/runtime/UuidGeneratorServiceLoader.java
+++ b/cucumber-core/src/main/java/io/cucumber/core/runtime/UuidGeneratorServiceLoader.java
@@ -38,7 +38,7 @@ public UuidGeneratorServiceLoader(Supplier classLoaderSupplier, Opt
this.options = requireNonNull(options);
}
- UuidGenerator loadUuidGenerator() {
+ public UuidGenerator loadUuidGenerator() {
Class extends UuidGenerator> objectFactoryClass = options.getUuidGeneratorClass();
ClassLoader classLoader = classLoaderSupplier.get();
ServiceLoader loader = ServiceLoader.load(UuidGenerator.class, classLoader);
diff --git a/cucumber-junit-platform-engine/README.md b/cucumber-junit-platform-engine/README.md
index e9a55b93cf..8e880f2ebf 100644
--- a/cucumber-junit-platform-engine/README.md
+++ b/cucumber-junit-platform-engine/README.md
@@ -373,6 +373,10 @@ cucumber.junit-platform.naming-strategy.long.example-name= # number or pickl
cucumber.plugin= # comma separated plugin strings.
# example: pretty, json:path/to/report.json
+
+cucumber.uuid-generator # uuid generator class name of a registered service provider.
+ # default: io.cucumber.core.eventbus.RandomUuidGenerator
+ # example: com.example.MyUuidGenerator
cucumber.object-factory= # object factory class name.
# example: com.example.MyObjectFactory
diff --git a/cucumber-junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/CucumberEngineExecutionContext.java b/cucumber-junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/CucumberEngineExecutionContext.java
index d8a5307c81..447b98d279 100644
--- a/cucumber-junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/CucumberEngineExecutionContext.java
+++ b/cucumber-junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/CucumberEngineExecutionContext.java
@@ -19,12 +19,12 @@
import io.cucumber.core.runtime.ThreadLocalObjectFactorySupplier;
import io.cucumber.core.runtime.ThreadLocalRunnerSupplier;
import io.cucumber.core.runtime.TimeServiceEventBus;
+import io.cucumber.core.runtime.UuidGeneratorServiceLoader;
import org.apiguardian.api.API;
import org.junit.platform.engine.ConfigurationParameters;
import org.junit.platform.engine.support.hierarchical.EngineExecutionContext;
import java.time.Clock;
-import java.util.UUID;
import java.util.function.Supplier;
import static io.cucumber.core.runtime.SynchronizedEventBus.synchronize;
@@ -48,8 +48,10 @@ CucumberEngineOptions getOptions() {
private CucumberExecutionContext createCucumberExecutionContext() {
Supplier classLoader = CucumberEngineExecutionContext.class::getClassLoader;
+ UuidGeneratorServiceLoader uuidGeneratorServiceLoader = new UuidGeneratorServiceLoader(classLoader, options);
+ EventBus bus = synchronize(
+ new TimeServiceEventBus(Clock.systemUTC(), uuidGeneratorServiceLoader.loadUuidGenerator()));
ObjectFactoryServiceLoader objectFactoryServiceLoader = new ObjectFactoryServiceLoader(classLoader, options);
- EventBus bus = synchronize(new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID));
Plugins plugins = new Plugins(new PluginFactory(), options);
ExitStatus exitStatus = new ExitStatus(options);
plugins.addPlugin(exitStatus);
diff --git a/cucumber-junit/src/main/java/io/cucumber/junit/Cucumber.java b/cucumber-junit/src/main/java/io/cucumber/junit/Cucumber.java
index f67376e141..d849d3124a 100644
--- a/cucumber-junit/src/main/java/io/cucumber/junit/Cucumber.java
+++ b/cucumber-junit/src/main/java/io/cucumber/junit/Cucumber.java
@@ -23,6 +23,7 @@
import io.cucumber.core.runtime.ThreadLocalObjectFactorySupplier;
import io.cucumber.core.runtime.ThreadLocalRunnerSupplier;
import io.cucumber.core.runtime.TimeServiceEventBus;
+import io.cucumber.core.runtime.UuidGeneratorServiceLoader;
import org.apiguardian.api.API;
import org.junit.AfterClass;
import org.junit.BeforeClass;
@@ -38,7 +39,6 @@
import java.util.List;
import java.util.Map;
import java.util.Optional;
-import java.util.UUID;
import java.util.function.Predicate;
import java.util.function.Supplier;
@@ -146,11 +146,14 @@ public Cucumber(Class> clazz) throws InitializationError {
.parse(CucumberProperties.fromSystemProperties())
.build(junitEnvironmentOptions);
- this.bus = synchronize(new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID));
+ Supplier classLoader = ClassLoaders::getDefaultClassLoader;
+ UuidGeneratorServiceLoader uuidGeneratorServiceLoader = new UuidGeneratorServiceLoader(classLoader,
+ runtimeOptions);
+ this.bus = synchronize(
+ new TimeServiceEventBus(Clock.systemUTC(), uuidGeneratorServiceLoader.loadUuidGenerator()));
// Parse the features early. Don't proceed when there are lexer errors
FeatureParser parser = new FeatureParser(bus::generateId);
- Supplier classLoader = ClassLoaders::getDefaultClassLoader;
FeaturePathFeatureSupplier featureSupplier = new FeaturePathFeatureSupplier(classLoader, runtimeOptions,
parser);
List features = featureSupplier.get();
diff --git a/cucumber-testng/src/main/java/io/cucumber/testng/TestNGCucumberRunner.java b/cucumber-testng/src/main/java/io/cucumber/testng/TestNGCucumberRunner.java
index 586095fb83..a098af11b0 100644
--- a/cucumber-testng/src/main/java/io/cucumber/testng/TestNGCucumberRunner.java
+++ b/cucumber-testng/src/main/java/io/cucumber/testng/TestNGCucumberRunner.java
@@ -23,11 +23,11 @@
import io.cucumber.core.runtime.ThreadLocalObjectFactorySupplier;
import io.cucumber.core.runtime.ThreadLocalRunnerSupplier;
import io.cucumber.core.runtime.TimeServiceEventBus;
+import io.cucumber.core.runtime.UuidGeneratorServiceLoader;
import org.apiguardian.api.API;
import java.time.Clock;
import java.util.List;
-import java.util.UUID;
import java.util.function.Predicate;
import java.util.function.Supplier;
@@ -98,9 +98,12 @@ public TestNGCucumberRunner(Class> clazz, CucumberPropertiesProvider propertie
.enablePublishPlugin()
.build(environmentOptions);
- EventBus bus = synchronize(new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID));
-
Supplier classLoader = ClassLoaders::getDefaultClassLoader;
+ UuidGeneratorServiceLoader uuidGeneratorServiceLoader = new UuidGeneratorServiceLoader(classLoader,
+ runtimeOptions);
+ EventBus bus = synchronize(
+ new TimeServiceEventBus(Clock.systemUTC(), uuidGeneratorServiceLoader.loadUuidGenerator()));
+
FeatureParser parser = new FeatureParser(bus::generateId);
FeaturePathFeatureSupplier featureSupplier = new FeaturePathFeatureSupplier(classLoader, runtimeOptions,
parser);