Skip to content

Commit

Permalink
feat: added UUID generator selection through SPI for #2698
Browse files Browse the repository at this point in the history
  • Loading branch information
Julien Kronegg committed Mar 17, 2023
1 parent 291b8f8 commit 39e815d
Show file tree
Hide file tree
Showing 80 changed files with 1,320 additions and 106 deletions.
24 changes: 24 additions & 0 deletions .revapi/api-changes.json
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,18 @@
"code": "java.method.finalMethodAddedToNonFinalClass",
"new": "method java.lang.Long io.cucumber.core.internal.com.fasterxml.jackson.databind.deser.std.StdDeserializer<T>::_parseLong(io.cucumber.core.internal.com.fasterxml.jackson.databind.DeserializationContext, java.lang.String) throws java.io.IOException",
"justification": "Internal API"
},
{
"ignore": true,
"code": "java.method.addedToInterface",
"new": "method java.lang.Class<? extends io.cucumber.core.eventbus.UuidGenerator> io.cucumber.core.options.CucumberOptionsAnnotationParser.CucumberOptions::uuidGenerator()",
"justification": "Internal API"
},
{
"ignore": true,
"code": "java.method.addedToInterface",
"new": "method java.lang.Class<? extends io.cucumber.core.eventbus.UuidGenerator> io.cucumber.core.runner.Options::getUuidGeneratorClass()",
"justification": "Internal API"
}
]
}
Expand Down Expand Up @@ -331,6 +343,12 @@
"code": "java.method.defaultMethodAddedToInterface",
"new": "method java.util.Set<org.testng.ITestNGMethod> org.testng.ITestNGMethod::upstreamDependencies()",
"justification": "Third party api change"
},
{
"ignore": true,
"code": "java.class.externalClassExposedInAPI",
"new": "interface io.cucumber.core.eventbus.UuidGenerator",
"justification": "Part of cucumber API"
}
]
}
Expand Down Expand Up @@ -383,6 +401,12 @@
"new": "method int org.junit.platform.engine.ConfigurationParameters::size()",
"annotation": "@org.apiguardian.api.API(status = org.apiguardian.api.API.Status.DEPRECATED, since = \"1.9\")",
"justification": "API consumed from JUnit 5"
},
{
"ignore": true,
"code": "java.class.externalClassExposedInAPI",
"new": "interface io.cucumber.core.eventbus.UuidGenerator",
"justification": "Part of cucumber API"
}
]
}
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added
- [Core] Improved event bus performance using UUID generator selectable through SPI ([#2703](https://github.com/cucumber/cucumber-jvm/pull/2703) Julien Kronegg)

## [7.11.1] - 2023-01-27
### Added
- [Core] Warn when `cucumber.options` is used ([#2685](https://github.com/cucumber/cucumber-jvm/pull/2685) M.P. Korstanje)
Expand Down
2 changes: 1 addition & 1 deletion compatibility/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<artifactId>cucumber-jvm</artifactId>
<groupId>io.cucumber</groupId>
<version>7.11.2-SNAPSHOT</version>
<version>7.12.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

Expand Down
2 changes: 1 addition & 1 deletion cucumber-archetype/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<parent>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-jvm</artifactId>
<version>7.11.2-SNAPSHOT</version>
<version>7.12.0-SNAPSHOT</version>
</parent>

<artifactId>cucumber-archetype</artifactId>
Expand Down
40 changes: 20 additions & 20 deletions cucumber-bom/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<parent>
<artifactId>cucumber-jvm</artifactId>
<groupId>io.cucumber</groupId>
<version>7.11.2-SNAPSHOT</version>
<version>7.12.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>pom</packaging>
Expand Down Expand Up @@ -63,97 +63,97 @@
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-cdi2</artifactId>
<version>7.11.2-SNAPSHOT</version>
<version>7.12.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-core</artifactId>
<version>7.11.2-SNAPSHOT</version>
<version>7.12.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>datatable</artifactId>
<version>7.11.2-SNAPSHOT</version>
<version>7.12.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>datatable-matchers</artifactId>
<version>7.11.2-SNAPSHOT</version>
<version>7.12.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-deltaspike</artifactId>
<version>7.11.2-SNAPSHOT</version>
<version>7.12.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>docstring</artifactId>
<version>7.11.2-SNAPSHOT</version>
<version>7.12.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-gherkin</artifactId>
<version>7.11.2-SNAPSHOT</version>
<version>7.12.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-gherkin-messages</artifactId>
<version>7.11.2-SNAPSHOT</version>
<version>7.12.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-guice</artifactId>
<version>7.11.2-SNAPSHOT</version>
<version>7.12.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-jakarta-cdi</artifactId>
<version>7.11.2-SNAPSHOT</version>
<version>7.12.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-java</artifactId>
<version>7.11.2-SNAPSHOT</version>
<version>7.12.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-java8</artifactId>
<version>7.11.2-SNAPSHOT</version>
<version>7.12.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-junit</artifactId>
<version>7.11.2-SNAPSHOT</version>
<version>7.12.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-junit-platform-engine</artifactId>
<version>7.11.2-SNAPSHOT</version>
<version>7.12.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-openejb</artifactId>
<version>7.11.2-SNAPSHOT</version>
<version>7.12.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-picocontainer</artifactId>
<version>7.11.2-SNAPSHOT</version>
<version>7.12.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-plugin</artifactId>
<version>7.11.2-SNAPSHOT</version>
<version>7.12.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-spring</artifactId>
<version>7.11.2-SNAPSHOT</version>
<version>7.12.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-testng</artifactId>
<version>7.11.2-SNAPSHOT</version>
<version>7.12.0-SNAPSHOT</version>
</dependency>
</dependencies>
</dependencyManagement>
Expand Down
2 changes: 1 addition & 1 deletion cucumber-cdi2/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<parent>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-jvm</artifactId>
<version>7.11.2-SNAPSHOT</version>
<version>7.12.0-SNAPSHOT</version>
</parent>

<artifactId>cucumber-cdi2</artifactId>
Expand Down
20 changes: 20 additions & 0 deletions cucumber-core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ cucumber.plugin= # comma separated plugin strings.
cucumber.object-factory= # object factory class name.
# example: com.example.MyObjectFactory
cucumber.uuid-generator= # UUID generator class name.
# example: com.example.MyUuidGenerator
cucumber.publish.enabled # true or false. default: false
# enable publishing of test results
Expand Down Expand Up @@ -79,6 +82,23 @@ They are respectively responsible for discovering glue classes, registering
step definitions, and creating instances of said glue classes. Backend and
object factory implementations are discovered via SPI.

## Event bus ##

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:

| 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 |

The performance gain on real project depend on the feature size.

When not specified, the `RandomUuidGenerator` is used.

## Plugin ##

By implementing the Plugin interface classes can listen to execution events
Expand Down
2 changes: 1 addition & 1 deletion cucumber-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-jvm</artifactId>
<version>7.11.2-SNAPSHOT</version>
<version>7.12.0-SNAPSHOT</version>
</parent>

<artifactId>cucumber-core</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ public final class CommandlineOptions {

public static final String OBJECT_FACTORY = "--object-factory";

public static final String UUID_GENERATOR = "--uuid-generator";

private CommandlineOptions() {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package io.cucumber.core.eventbus;

import io.cucumber.core.exception.CucumberException;

import java.util.UUID;
import java.util.concurrent.atomic.AtomicLong;

/**
* 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. If you use Cucumber in
* multi-JVM setup, you should use #RandomUuidGenerator instead. Note that the
* UUID version and variant is not guaranteed to be stable.
*/
public class IncrementingUuidGenerator implements UuidGenerator {
private static final AtomicLong sessionCounter = new AtomicLong(Long.MIN_VALUE);

private final long sessionId;
private final AtomicLong counter = new AtomicLong(Long.MIN_VALUE);

public IncrementingUuidGenerator() {
sessionId = sessionCounter.incrementAndGet();
}

/**
* Generate a new UUID. Will throw an exception when out of capacity.
*
* @return a non-null UUID
* @throws CucumberException when out of capacity
*/
@Override
public UUID get() {
long leastSigBits = counter.incrementAndGet();
if (leastSigBits == Long.MAX_VALUE) {
throw new CucumberException(
"Out of IncrementingUuidGenerator capacity. Please use the RandomUuidGenerator instead.");
}
return new UUID(sessionId, leastSigBits);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package io.cucumber.core.eventbus;

public interface Options {

Class<? extends UuidGenerator> getUuidGeneratorClass();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package io.cucumber.core.eventbus;

import java.util.UUID;

/**
* UUID generator based on random numbers. The generator is thread-safe and
* supports multi-jvm usage of Cucumber.
*/
public class RandomUuidGenerator implements UuidGenerator {
@Override
public UUID get() {
return UUID.randomUUID();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package io.cucumber.core.eventbus;

import org.apiguardian.api.API;

import java.util.UUID;
import java.util.function.Supplier;

/**
* SPI (Service Provider Interface) to generate UUIDs.
*/
@API(status = API.Status.STABLE)
public interface UuidGenerator extends Supplier<UUID> {
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,14 @@
import static io.cucumber.core.cli.CommandlineOptions.TAGS;
import static io.cucumber.core.cli.CommandlineOptions.TAGS_SHORT;
import static io.cucumber.core.cli.CommandlineOptions.THREADS;
import static io.cucumber.core.cli.CommandlineOptions.UUID_GENERATOR;
import static io.cucumber.core.cli.CommandlineOptions.VERSION;
import static io.cucumber.core.cli.CommandlineOptions.VERSION_SHORT;
import static io.cucumber.core.cli.CommandlineOptions.WIP;
import static io.cucumber.core.cli.CommandlineOptions.WIP_SHORT;
import static io.cucumber.core.options.ObjectFactoryParser.parseObjectFactory;
import static io.cucumber.core.options.OptionsFileParser.parseFeatureWithLinesFile;
import static io.cucumber.core.options.UuidGeneratorParser.parseUuidGenerator;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Arrays.asList;
import static java.util.stream.Collectors.joining;
Expand Down Expand Up @@ -167,6 +169,9 @@ private RuntimeOptionsBuilder parse(List<String> args) {
} else if (arg.equals(OBJECT_FACTORY)) {
String objectFactoryClassName = removeArgFor(arg, args);
parsedOptions.setObjectFactoryClass(parseObjectFactory(objectFactoryClassName));
} else if (arg.equals(UUID_GENERATOR)) {
String uuidGeneratorClassName = removeArgFor(arg, args);
parsedOptions.setUuidGeneratorClass(parseUuidGenerator(uuidGeneratorClassName));
} else if (arg.startsWith("-")) {
out.println("Unknown option: " + arg);
printUsage();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.cucumber.core.options;

import io.cucumber.core.runtime.ObjectFactoryServiceLoader;
import io.cucumber.core.runtime.UuidGeneratorServiceLoader;

public final class Constants {

Expand Down Expand Up @@ -118,6 +119,14 @@ public final class Constants {
*/
public static final String OBJECT_FACTORY_PROPERTY_NAME = "cucumber.object-factory";

/**
* Property name used to select a specific UUID generator implementation:
* {@value}
*
* @see UuidGeneratorServiceLoader
*/
public static final String UUID_GENERATOR_PROPERTY_NAME = "cucumber.uuid-generator";

/**
* Property name formerly used to pass command line options to Cucumber:
* {@value} This property is no longer read by Cucumber. Please use any of
Expand Down
Loading

0 comments on commit 39e815d

Please sign in to comment.