From 5687ee95612b406bd2939d03a3bc0cc4c2b323a7 Mon Sep 17 00:00:00 2001 From: "M.P. Korstanje" Date: Tue, 10 Aug 2021 23:04:08 +0200 Subject: [PATCH] [Core] Synchronize event bus before use We can not exclude the possibility that an event is published to the event bus after test execution has completed. For example because the test framework may have decided that a test case has timed out and is now terminating test execution. As such events from the pending testcase on the synchronized bus may race with the events from the unsynchronized bus. This in turn may break the `LinkedList` in the `CanonicalOrderEventPublisher`. Fixes: #2288 (mabye??) --- CHANGELOG.md | 1 + core/src/main/java/io/cucumber/core/runtime/Runtime.java | 4 ++++ junit/src/main/java/io/cucumber/junit/Cucumber.java | 4 +++- .../main/java/io/cucumber/testng/TestNGCucumberRunner.java | 4 +++- 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 393f0e4576..50b7ba6a9f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Removed ### Fixed +* [Core] Synchronize event bus before use ([#2358](https://github.com/cucumber/cucumber-jvm/pull/2358)) M.P. Korstanje) ## [6.11.0] (2021-08-05) diff --git a/core/src/main/java/io/cucumber/core/runtime/Runtime.java b/core/src/main/java/io/cucumber/core/runtime/Runtime.java index 2e9630be39..30f5261d68 100644 --- a/core/src/main/java/io/cucumber/core/runtime/Runtime.java +++ b/core/src/main/java/io/cucumber/core/runtime/Runtime.java @@ -31,6 +31,7 @@ import java.util.function.Predicate; import java.util.function.Supplier; +import static io.cucumber.core.runtime.SynchronizedEventBus.synchronize; import static java.util.Collections.emptyList; import static java.util.stream.Collectors.collectingAndThen; import static java.util.stream.Collectors.toList; @@ -174,6 +175,9 @@ public Runtime build() { } final ExitStatus exitStatus = new ExitStatus(runtimeOptions); plugins.addPlugin(exitStatus); + + final EventBus eventBus = synchronize(this.eventBus); + if (runtimeOptions.isMultiThreaded()) { plugins.setSerialEventBusOnEventListenerPlugins(eventBus); } else { diff --git a/junit/src/main/java/io/cucumber/junit/Cucumber.java b/junit/src/main/java/io/cucumber/junit/Cucumber.java index 807417fe8c..2c823ed231 100644 --- a/junit/src/main/java/io/cucumber/junit/Cucumber.java +++ b/junit/src/main/java/io/cucumber/junit/Cucumber.java @@ -21,6 +21,7 @@ import io.cucumber.core.runtime.ObjectFactoryServiceLoader; import io.cucumber.core.runtime.ObjectFactorySupplier; import io.cucumber.core.runtime.ScanningTypeRegistryConfigurerSupplier; +import io.cucumber.core.runtime.SynchronizedEventBus; import io.cucumber.core.runtime.ThreadLocalObjectFactorySupplier; import io.cucumber.core.runtime.ThreadLocalRunnerSupplier; import io.cucumber.core.runtime.TimeServiceEventBus; @@ -44,6 +45,7 @@ import java.util.function.Predicate; import java.util.function.Supplier; +import static io.cucumber.core.runtime.SynchronizedEventBus.synchronize; import static io.cucumber.junit.FileNameCompatibleNames.uniqueSuffix; import static java.util.stream.Collectors.groupingBy; import static java.util.stream.Collectors.toList; @@ -148,7 +150,7 @@ public Cucumber(Class clazz) throws InitializationError { .parse(CucumberProperties.fromSystemProperties()) .build(junitEnvironmentOptions); - this.bus = new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID); + this.bus = synchronize(new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID)); // Parse the features early. Don't proceed when there are lexer errors FeatureParser parser = new FeatureParser(bus::generateId); diff --git a/testng/src/main/java/io/cucumber/testng/TestNGCucumberRunner.java b/testng/src/main/java/io/cucumber/testng/TestNGCucumberRunner.java index 3ce9bebb56..8c42e84132 100644 --- a/testng/src/main/java/io/cucumber/testng/TestNGCucumberRunner.java +++ b/testng/src/main/java/io/cucumber/testng/TestNGCucumberRunner.java @@ -21,6 +21,7 @@ import io.cucumber.core.runtime.ObjectFactoryServiceLoader; import io.cucumber.core.runtime.ObjectFactorySupplier; import io.cucumber.core.runtime.ScanningTypeRegistryConfigurerSupplier; +import io.cucumber.core.runtime.SynchronizedEventBus; import io.cucumber.core.runtime.ThreadLocalObjectFactorySupplier; import io.cucumber.core.runtime.ThreadLocalRunnerSupplier; import io.cucumber.core.runtime.TimeServiceEventBus; @@ -33,6 +34,7 @@ import java.util.function.Predicate; import java.util.function.Supplier; +import static io.cucumber.core.runtime.SynchronizedEventBus.synchronize; import static io.cucumber.testng.TestCaseResultObserver.observe; import static java.util.stream.Collectors.toList; @@ -99,7 +101,7 @@ public TestNGCucumberRunner(Class clazz, CucumberPropertiesProvider propertie .enablePublishPlugin() .build(environmentOptions); - EventBus bus = new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID); + EventBus bus = synchronize(new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID)); Supplier classLoader = ClassLoaders::getDefaultClassLoader; FeatureParser parser = new FeatureParser(bus::generateId);