Skip to content

This is the fix for issue #723. I have made the static GroovyBackend instance a ThreadLocal so it can be run in parallel in the same JVM. #727

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 25, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions groovy/src/main/code_generator/I18n.groovy.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import java.util.regex.Pattern;
public class ${i18n.underscoredIsoCode.toUpperCase()} {
<% i18n.codeKeywords.each { kw -> %>
public static void ${kw}(Pattern regexp, Closure body) throws Throwable {
GroovyBackend.instance.addStepDefinition(regexp, 0, body);
GroovyBackend.getInstance().addStepDefinition(regexp, 0, body);
}

public static void ${java.text.Normalizer.normalize(kw, java.text.Normalizer.Form.NFC)}(Pattern regexp, long timeoutMillis, Closure body) throws Throwable {
GroovyBackend.instance.addStepDefinition(regexp, timeoutMillis, body);
GroovyBackend.getInstance().addStepDefinition(regexp, timeoutMillis, body);
}
<% } %>
}
6 changes: 3 additions & 3 deletions groovy/src/main/java/cucumber/api/groovy/Hooks.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

public class Hooks {
public static void World(Closure body) throws Throwable {
GroovyBackend.instance.registerWorld(body);
GroovyBackend.getInstance().registerWorld(body);
}

public static void Before(Object... args) throws Throwable {
Expand Down Expand Up @@ -37,9 +37,9 @@ private static void addHook(Object[] tagsExpressionsAndBody, boolean before) {

TagExpression tagExpression = new TagExpression(tagExpressions);
if (before) {
GroovyBackend.instance.addBeforeHook(tagExpression, timeoutMillis, body);
GroovyBackend.getInstance().addBeforeHook(tagExpression, timeoutMillis, body);
} else {
GroovyBackend.instance.addAfterHook(tagExpression, timeoutMillis, body);
GroovyBackend.getInstance().addAfterHook(tagExpression, timeoutMillis, body);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
import static cucumber.runtime.io.MultiLoader.packageName;

public class GroovyBackend implements Backend {
public static GroovyBackend instance;
public static ThreadLocal<GroovyBackend> instanceThreadLocal = new ThreadLocal<GroovyBackend>();
private final Set<Class> scripts = new HashSet<Class>();
private final SnippetGenerator snippetGenerator = new SnippetGenerator(new GroovySnippet());
private final ResourceLoader resourceLoader;
Expand All @@ -41,6 +41,10 @@ public class GroovyBackend implements Backend {
private Object world;
private Glue glue;

public static GroovyBackend getInstance(){
return instanceThreadLocal.get();
}

private static GroovyShell createShell() {
CompilerConfiguration compilerConfig = new CompilerConfiguration();
// Probably not needed:
Expand All @@ -55,7 +59,7 @@ public GroovyBackend(ResourceLoader resourceLoader) {
public GroovyBackend(GroovyShell shell, ResourceLoader resourceLoader) {
this.shell = shell;
this.resourceLoader = resourceLoader;
instance = this;
instanceThreadLocal.set(this);
classFinder = new ResourceLoaderClassFinder(resourceLoader, shell.getClassLoader());
}

Expand Down
51 changes: 51 additions & 0 deletions groovy/src/test/java/cucumber/runtime/groovy/ParallelTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package cucumber.runtime.groovy;

import cucumber.runtime.io.ResourceLoader;
import groovy.lang.Closure;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;


@RunWith(MockitoJUnitRunner.class)
public class ParallelTest {
@Mock
ResourceLoader resourceLoader;
@Mock
Closure closure;

@Test(expected = RuntimeException.class)
public void exception_throw_when_world_already_set_on_same_thread() {
GroovyBackend groovyBackend = new GroovyBackend(resourceLoader);
groovyBackend.registerWorld(closure);
groovyBackend.registerWorld(closure);
}

@Test
public void can_have_a_new_backend_on_a_different_thread() {
new GroovyBackend(resourceLoader);
Thread interactWithBackendThread = new Thread(new Runnable(){
@Override
public void run() {
try {
GroovyBackend.getInstance().registerWorld(closure);
} catch (NullPointerException e){
// This is what we want as there should be no GroovyBackend on this thread
}
}
});
runAndWait(interactWithBackendThread);
GroovyBackend.getInstance().registerWorld(closure);
}

private void runAndWait(Thread interactWithBackendThread) {
interactWithBackendThread.start();
try {
interactWithBackendThread.join();
} catch (InterruptedException e) {
throw new RuntimeException("Doh");
}
}

}