Skip to content

Commit 45c9abb

Browse files
mpkorstanjeJulien Kronegg
authored andcommitted
Faster caching glue
1 parent daeb1e4 commit 45c9abb

13 files changed

+183
-122
lines changed

cucumber-core/src/main/java/io/cucumber/core/runner/CachingGlue.java

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import java.util.HashMap;
4545
import java.util.Iterator;
4646
import java.util.List;
47+
import java.util.Locale;
4748
import java.util.Map;
4849
import java.util.TreeMap;
4950

@@ -81,6 +82,7 @@ final class CachingGlue implements Glue {
8182
private final Map<String, CoreStepDefinition> stepDefinitionsByPattern = new TreeMap<>();
8283

8384
private final EventBus bus;
85+
private StepTypeRegistry stepTypeRegistry;
8486

8587
CachingGlue(EventBus bus) {
8688
this.bus = bus;
@@ -228,7 +230,12 @@ List<DocStringTypeDefinition> getDocStringTypeDefinitions() {
228230
return docStringTypeDefinitions;
229231
}
230232

231-
void prepareGlue(StepTypeRegistry stepTypeRegistry) throws DuplicateStepDefinitionException {
233+
StepTypeRegistry getStepTypeRegistry() {
234+
return null;
235+
}
236+
237+
void prepareGlue(Locale locale) throws DuplicateStepDefinitionException {
238+
stepTypeRegistry = new StepTypeRegistry(locale);
232239
StepExpressionFactory stepExpressionFactory = new StepExpressionFactory(stepTypeRegistry, bus);
233240

234241
// TODO: separate prepared and unprepared glue into different classes
@@ -425,7 +432,6 @@ private List<PickleStepDefinitionMatch> stepDefinitionMatches(URI uri, Step step
425432
}
426433

427434
void removeScenarioScopedGlue() {
428-
stepDefinitionsByPattern.clear();
429435
removeScenarioScopedGlue(beforeHooks);
430436
removeScenarioScopedGlue(beforeStepHooks);
431437
removeScenarioScopedGlue(afterHooks);
@@ -437,18 +443,24 @@ void removeScenarioScopedGlue() {
437443
removeScenarioScopedGlue(defaultParameterTransformers);
438444
removeScenarioScopedGlue(defaultDataTableEntryTransformers);
439445
removeScenarioScopedGlue(defaultDataTableCellTransformers);
446+
447+
stepDefinitionsByPattern.clear();
448+
440449
}
441450

442-
private void removeScenarioScopedGlue(Iterable<?> glues) {
451+
private boolean removeScenarioScopedGlue(Iterable<?> glues) {
452+
boolean dirty = false;
443453
Iterator<?> glueIterator = glues.iterator();
444454
while (glueIterator.hasNext()) {
445455
Object glue = glueIterator.next();
446456
if (glue instanceof ScenarioScoped) {
447457
ScenarioScoped scenarioScoped = (ScenarioScoped) glue;
448458
scenarioScoped.dispose();
449459
glueIterator.remove();
460+
dirty = true;
450461
}
451462
}
463+
return dirty;
452464
}
453465

454466
}

cucumber-core/src/main/java/io/cucumber/core/runner/Runner.java

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,13 +63,12 @@ public EventBus getBus() {
6363

6464
public void runPickle(Pickle pickle) {
6565
try {
66-
StepTypeRegistry stepTypeRegistry = createTypeRegistryForPickle(pickle);
67-
snippetGenerators = createSnippetGeneratorsForPickle(stepTypeRegistry);
6866

6967
// Java8 step definitions will be added to the glue here
7068
buildBackendWorlds();
7169

72-
glue.prepareGlue(stepTypeRegistry);
70+
glue.prepareGlue(localeForPickle(pickle));
71+
snippetGenerators = createSnippetGeneratorsForPickle(glue.getStepTypeRegistry());
7372

7473
TestCase testCase = createTestCaseForPickle(pickle);
7574
testCase.run(bus);
@@ -79,10 +78,9 @@ public void runPickle(Pickle pickle) {
7978
}
8079
}
8180

82-
private StepTypeRegistry createTypeRegistryForPickle(Pickle pickle) {
81+
private Locale localeForPickle(Pickle pickle) {
8382
String language = pickle.getLanguage();
84-
Locale locale = new Locale(language);
85-
return new StepTypeRegistry(locale);
83+
return new Locale(language);
8684
}
8785

8886
public void runBeforeAllHooks() {

cucumber-java8/src/main/java/io/cucumber/java8/AbstractGlueDefinition.java

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package io.cucumber.java8;
22

33
import io.cucumber.core.backend.Located;
4-
import io.cucumber.core.backend.ScenarioScoped;
54
import io.cucumber.core.backend.SourceReference;
65
import net.jodah.typetools.TypeResolver;
76

@@ -14,20 +13,33 @@
1413
import static java.lang.String.format;
1514
import static java.util.Objects.requireNonNull;
1615

17-
abstract class AbstractGlueDefinition implements ScenarioScoped, Located {
16+
abstract class AbstractGlueDefinition implements Located {
1817

1918
private Object body;
20-
final Method method;
19+
private Method method;
20+
private SourceReference sourceReference;
2121
final StackTraceElement location;
22-
SourceReference sourceReference;
2322

2423
AbstractGlueDefinition(Object body, StackTraceElement location) {
24+
updateClosure(body);
25+
this.location = requireNonNull(location);
26+
}
27+
28+
void updateClosure(AbstractGlueDefinition other) {
29+
updateClosure(other.body);
30+
}
31+
32+
private void updateClosure(Object body) {
2533
this.body = requireNonNull(body);
2634
this.method = getAcceptMethod(body.getClass());
27-
this.location = requireNonNull(location);
2835
}
2936

30-
private Method getAcceptMethod(Class<?> bodyClass) {
37+
void disposeClosure() {
38+
this.body = null;
39+
this.method = null;
40+
}
41+
42+
private static Method getAcceptMethod(Class<?> bodyClass) {
3143
List<Method> acceptMethods = new ArrayList<>();
3244
for (Method method : bodyClass.getDeclaredMethods()) {
3345
if (!method.isBridge() && !method.isSynthetic() && "accept".equals(method.getName())) {
@@ -48,9 +60,8 @@ protected Object invokeMethod(Object... args) {
4860
return Invoker.invoke(this, body, method, args);
4961
}
5062

51-
@Override
52-
public void dispose() {
53-
this.body = null;
63+
protected int getParameterCount() {
64+
return method.getParameterCount();
5465
}
5566

5667
@Override
@@ -65,10 +76,14 @@ public final boolean isDefinedAt(StackTraceElement stackTraceElement) {
6576

6677
@Override
6778
public Optional<SourceReference> getSourceReference() {
79+
return Optional.of(requireSourceReference());
80+
}
81+
82+
SourceReference requireSourceReference() {
6883
if (sourceReference == null) {
6984
sourceReference = fromStackTraceElement(location);
7085
}
71-
return Optional.of(sourceReference);
86+
return sourceReference;
7287
}
7388

7489
Class<?>[] resolveRawArguments(Class<?> bodyClass, Class<?> body) {
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
package io.cucumber.java8;
2+
3+
import io.cucumber.core.backend.DataTableTypeDefinition;
4+
import io.cucumber.core.backend.DefaultDataTableCellTransformerDefinition;
5+
import io.cucumber.core.backend.DefaultDataTableEntryTransformerDefinition;
6+
import io.cucumber.core.backend.DefaultParameterTransformerDefinition;
7+
import io.cucumber.core.backend.DocStringTypeDefinition;
8+
import io.cucumber.core.backend.Glue;
9+
import io.cucumber.core.backend.HookDefinition;
10+
import io.cucumber.core.backend.ParameterTypeDefinition;
11+
import io.cucumber.core.backend.StepDefinition;
12+
13+
import java.util.ArrayList;
14+
import java.util.List;
15+
import java.util.function.Consumer;
16+
17+
final class ClosureAwareGlueRegistry implements LambdaGlueRegistry {
18+
19+
private final List<AbstractGlueDefinition> definitions = new ArrayList<>();
20+
private int registered;
21+
22+
private final Glue glue;
23+
24+
ClosureAwareGlueRegistry(Glue glue) {
25+
this.glue = glue;
26+
}
27+
28+
void startRegistration() {
29+
registered = 0;
30+
}
31+
32+
@Override
33+
public void addStepDefinition(StepDefinition stepDefinition) {
34+
updateOrRegister((Java8StepDefinition) stepDefinition, definitions, glue::addStepDefinition);
35+
}
36+
37+
@Override
38+
public void addBeforeStepHookDefinition(HookDefinition beforeStepHook) {
39+
updateOrRegister((Java8HookDefinition) beforeStepHook, definitions, glue::addBeforeStepHook);
40+
41+
}
42+
43+
@Override
44+
public void addAfterStepHookDefinition(HookDefinition afterStepHook) {
45+
updateOrRegister((Java8HookDefinition) afterStepHook, definitions, glue::addAfterStepHook);
46+
}
47+
48+
@Override
49+
public void addBeforeHookDefinition(HookDefinition beforeHook) {
50+
updateOrRegister((Java8HookDefinition) beforeHook, definitions, glue::addBeforeHook);
51+
}
52+
53+
@Override
54+
public void addAfterHookDefinition(HookDefinition afterHook) {
55+
updateOrRegister((Java8HookDefinition) afterHook, definitions, glue::addAfterHook);
56+
}
57+
58+
@Override
59+
public void addDocStringType(DocStringTypeDefinition docStringType) {
60+
updateOrRegister((Java8DocStringTypeDefinition) docStringType, definitions, glue::addDocStringType);
61+
}
62+
63+
@Override
64+
public void addDataTableType(DataTableTypeDefinition dataTableType) {
65+
updateOrRegister((Java8DataTableTypeDefinition) dataTableType, definitions, glue::addDataTableType);
66+
}
67+
68+
@Override
69+
public void addParameterType(ParameterTypeDefinition parameterType) {
70+
updateOrRegister((Java8ParameterTypeDefinition) parameterType, definitions, glue::addParameterType);
71+
}
72+
73+
@Override
74+
public void addDefaultParameterTransformer(DefaultParameterTransformerDefinition defaultParameterTransformer) {
75+
updateOrRegister((Java8DefaultParameterTransformerDefinition) defaultParameterTransformer, definitions,
76+
glue::addDefaultParameterTransformer);
77+
}
78+
79+
@Override
80+
public void addDefaultDataTableCellTransformer(
81+
DefaultDataTableCellTransformerDefinition defaultDataTableCellTransformer
82+
) {
83+
updateOrRegister((Java8DefaultDataTableCellTransformerDefinition) defaultDataTableCellTransformer, definitions,
84+
glue::addDefaultDataTableCellTransformer);
85+
}
86+
87+
@Override
88+
public void addDefaultDataTableEntryTransformer(
89+
DefaultDataTableEntryTransformerDefinition defaultDataTableEntryTransformer
90+
) {
91+
updateOrRegister(
92+
(Java8DefaultDataTableEntryTransformerDefinition) defaultDataTableEntryTransformer,
93+
definitions,
94+
glue::addDefaultDataTableEntryTransformer);
95+
}
96+
97+
private <T extends AbstractGlueDefinition> void updateOrRegister(
98+
T definition, List<AbstractGlueDefinition> definitions, Consumer<T> register
99+
) {
100+
if (definitions.size() <= registered) {
101+
definitions.add(definition);
102+
register.accept(definition);
103+
} else {
104+
AbstractGlueDefinition existing = definitions.get(registered);
105+
existing.updateClosure(definition);
106+
}
107+
registered++;
108+
}
109+
110+
void disposeClosures() {
111+
definitions.forEach(AbstractGlueDefinition::disposeClosure);
112+
}
113+
}

0 commit comments

Comments
 (0)