Skip to content

Commit a014d71

Browse files
committed
Support manual glue class registration
1 parent b2bed6a commit a014d71

File tree

10 files changed

+121
-22
lines changed

10 files changed

+121
-22
lines changed

cucumber-core/src/main/java/io/cucumber/core/backend/Backend.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import java.net.URI;
66
import java.util.List;
7+
import java.util.Set;
78

89
@API(status = API.Status.STABLE)
910
public interface Backend {
@@ -15,7 +16,20 @@ public interface Backend {
1516
* @param glue Glue that provides the steps to be executed.
1617
* @param gluePaths The locations for the glue to be loaded.
1718
*/
18-
void loadGlue(Glue glue, List<URI> gluePaths);
19+
default void loadGlue(Glue glue, List<URI> gluePaths) {
20+
21+
}
22+
23+
/**
24+
* Invoked once before all features. This is where steps and hooks should be
25+
* loaded.
26+
*
27+
* @param glue Glue that provides the steps to be executed.
28+
* @param glueClassNames The classes of glue to be loaded.
29+
*/
30+
default void loadGlueClasses(Glue glue, Set<String> glueClassNames) {
31+
// TODO: Refactor out a request object.
32+
}
1933

2034
/**
2135
* Invoked before a new scenario starts. Implementations should do any

cucumber-core/src/main/java/io/cucumber/core/cli/CommandlineOptions.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ public final class CommandlineOptions {
3535
public static final String GLUE = "--glue";
3636
public static final String GLUE_SHORT = "-g";
3737

38+
public static final String GLUE_CLASS = "--glue-class";
39+
3840
public static final String TAGS = "--tags";
3941
public static final String TAGS_SHORT = "-t";
4042

cucumber-core/src/main/java/io/cucumber/core/options/CommandlineOptionsParser.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import static io.cucumber.core.cli.CommandlineOptions.DRY_RUN;
3131
import static io.cucumber.core.cli.CommandlineOptions.DRY_RUN_SHORT;
3232
import static io.cucumber.core.cli.CommandlineOptions.GLUE;
33+
import static io.cucumber.core.cli.CommandlineOptions.GLUE_CLASS;
3334
import static io.cucumber.core.cli.CommandlineOptions.GLUE_SHORT;
3435
import static io.cucumber.core.cli.CommandlineOptions.HELP;
3536
import static io.cucumber.core.cli.CommandlineOptions.HELP_SHORT;
@@ -125,6 +126,9 @@ private RuntimeOptionsBuilder parse(List<String> args) {
125126
String gluePath = removeArgFor(arg, args);
126127
URI parse = GluePath.parse(gluePath);
127128
parsedOptions.addGlue(parse);
129+
} else if (arg.equals(GLUE_CLASS)) {
130+
String glueClassName = removeArgFor(arg, args);
131+
parsedOptions.addGlueClass(glueClassName);
128132
} else if (arg.equals(TAGS) || arg.equals(TAGS_SHORT)) {
129133
parsedOptions.addTagFilter(TagExpressionParser.parse(removeArgFor(arg, args)));
130134
} else if (arg.equals(PUBLISH)) {

cucumber-core/src/main/java/io/cucumber/core/options/RuntimeOptions.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import java.net.URI;
1515
import java.util.ArrayList;
1616
import java.util.HashMap;
17+
import java.util.HashSet;
1718
import java.util.LinkedHashSet;
1819
import java.util.List;
1920
import java.util.Map;
@@ -30,6 +31,7 @@
3031
import static java.util.Collections.singletonList;
3132
import static java.util.Collections.unmodifiableList;
3233
import static java.util.Collections.unmodifiableMap;
34+
import static java.util.Collections.unmodifiableSet;
3335

3436
public final class RuntimeOptions implements
3537
io.cucumber.core.feature.Options,
@@ -40,6 +42,7 @@ public final class RuntimeOptions implements
4042
io.cucumber.core.eventbus.Options {
4143

4244
private final List<URI> glue = new ArrayList<>();
45+
private final Set<String> glueClasses = new HashSet<>();
4346
private final List<Expression> tagExpressions = new ArrayList<>();
4447
private final List<Pattern> nameFilters = new ArrayList<>();
4548
private final List<FeatureWithLines> featurePaths = new ArrayList<>();
@@ -150,6 +153,11 @@ public List<URI> getGlue() {
150153
return unmodifiableList(glue);
151154
}
152155

156+
@Override
157+
public Set<String> getGlueClasses() {
158+
return unmodifiableSet(glueClasses);
159+
}
160+
153161
@Override
154162
public boolean isDryRun() {
155163
return dryRun;
@@ -191,6 +199,11 @@ void setGlue(List<URI> parsedGlue) {
191199
glue.addAll(parsedGlue);
192200
}
193201

202+
void setGlueClasses(Set<String> parsedGlue) {
203+
glueClasses.clear();
204+
glueClasses.addAll(parsedGlue);
205+
}
206+
194207
@Override
195208
public List<URI> getFeaturePaths() {
196209
return unmodifiableList(featurePaths.stream()

cucumber-core/src/main/java/io/cucumber/core/options/RuntimeOptionsBuilder.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import java.net.URI;
1313
import java.util.ArrayList;
1414
import java.util.Collection;
15+
import java.util.HashSet;
1516
import java.util.List;
1617
import java.util.Set;
1718
import java.util.regex.Pattern;
@@ -22,6 +23,7 @@ public final class RuntimeOptionsBuilder {
2223
private final List<Pattern> parsedNameFilters = new ArrayList<>();
2324
private final List<FeatureWithLines> parsedFeaturePaths = new ArrayList<>();
2425
private final List<URI> parsedGlue = new ArrayList<>();
26+
private final Set<String> parsedGlueClasses = new HashSet<>();
2527
private final List<Options.Plugin> plugins = new ArrayList<>();
2628
private List<FeatureWithLines> parsedRerunPaths = null;
2729
private Integer parsedThreads = null;
@@ -59,6 +61,12 @@ public RuntimeOptionsBuilder addGlue(URI glue) {
5961
return this;
6062
}
6163

64+
public RuntimeOptionsBuilder addGlueClass(String glueClassName) {
65+
// TODO: Support Class<?> ?
66+
parsedGlueClasses.add(glueClassName);
67+
return this;
68+
}
69+
6270
public RuntimeOptionsBuilder addNameFilter(Pattern pattern) {
6371
this.parsedNameFilters.add(pattern);
6472
return this;
@@ -128,6 +136,10 @@ public RuntimeOptions build(RuntimeOptions runtimeOptions) {
128136
runtimeOptions.setGlue(this.parsedGlue);
129137
}
130138

139+
if (!this.parsedGlueClasses.isEmpty()) {
140+
runtimeOptions.setGlueClasses(this.parsedGlueClasses);
141+
}
142+
131143
runtimeOptions.addPlugins(this.plugins);
132144

133145
if (parsedObjectFactoryClass != null) {

cucumber-core/src/main/java/io/cucumber/core/resource/ClasspathScanner.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ private Function<Path, Consumer<Path>> processClassFiles(
101101
};
102102
}
103103

104-
private Optional<Class<?>> safelyLoadClass(String fqn) {
104+
public Optional<Class<?>> safelyLoadClass(String fqn) {
105105
try {
106106
return Optional.ofNullable(getClassLoader().loadClass(fqn));
107107
} catch (ClassNotFoundException | NoClassDefFoundError e) {

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
import io.cucumber.core.snippets.SnippetType;
66

77
import java.net.URI;
8+
import java.util.Collections;
89
import java.util.List;
10+
import java.util.Set;
911

1012
public interface Options {
1113

@@ -19,4 +21,7 @@ public interface Options {
1921

2022
Class<? extends UuidGenerator> getUuidGeneratorClass();
2123

24+
default Set<String> getGlueClasses() {
25+
return Collections.emptySet();
26+
}
2227
}

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import io.cucumber.plugin.event.SnippetsSuggestedEvent;
2020
import io.cucumber.plugin.event.SnippetsSuggestedEvent.Suggestion;
2121

22-
import java.net.URI;
2322
import java.util.ArrayList;
2423
import java.util.Collection;
2524
import java.util.List;
@@ -50,11 +49,11 @@ public Runner(
5049
this.backends = backends;
5150
this.glue = new CachingGlue(bus);
5251
this.objectFactory = objectFactory;
53-
List<URI> gluePaths = runnerOptions.getGlue();
54-
log.debug(() -> "Loading glue from " + gluePaths);
52+
log.debug(() -> "Loading glue from " + runnerOptions.getGlue());
5553
for (Backend backend : backends) {
5654
log.debug(() -> "Loading glue for backend " + backend.getClass().getName());
57-
backend.loadGlue(this.glue, gluePaths);
55+
backend.loadGlue(this.glue, runnerOptions.getGlue());
56+
backend.loadGlueClasses(this.glue, runnerOptions.getGlueClasses());
5857
}
5958
}
6059

cucumber-java/src/main/java/io/cucumber/java/JavaBackend.java

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@
1111
import java.net.URI;
1212
import java.util.Collection;
1313
import java.util.List;
14+
import java.util.Optional;
15+
import java.util.Set;
1416
import java.util.function.Supplier;
17+
import java.util.stream.Collectors;
1518

1619
import static io.cucumber.core.resource.ClasspathSupport.CLASSPATH_SCHEME;
1720
import static io.cucumber.java.MethodScanner.scan;
@@ -30,18 +33,39 @@ final class JavaBackend implements Backend {
3033

3134
@Override
3235
public void loadGlue(Glue glue, List<URI> gluePaths) {
36+
loadGlueClassesImpl(glue, scanForClasses(gluePaths));
37+
}
38+
39+
@Override
40+
public void loadGlueClasses(Glue glue, Set<String> glueClassNames) {
41+
Set<Class<?>> glueClasses = glueClassNames.stream()
42+
.map(classFinder::safelyLoadClass)
43+
.filter(Optional::isPresent)
44+
.map(Optional::get)
45+
.collect(Collectors.toSet());
46+
47+
loadGlueClassesImpl(glue, glueClasses);
48+
}
49+
50+
private void loadGlueClassesImpl(Glue glue, Set<Class<?>> glueClasses) {
3351
GlueAdaptor glueAdaptor = new GlueAdaptor(lookup, glue);
52+
glueClasses.forEach(aGlueClass -> processClass(aGlueClass, glueAdaptor));
53+
}
3454

35-
gluePaths.stream()
55+
private Set<Class<?>> scanForClasses(List<URI> gluePaths) {
56+
return gluePaths.stream()
3657
.filter(gluePath -> CLASSPATH_SCHEME.equals(gluePath.getScheme()))
3758
.map(ClasspathSupport::packageName)
3859
.map(classFinder::scanForClassesInPackage)
3960
.flatMap(Collection::stream)
40-
.distinct()
41-
.forEach(aGlueClass -> scan(aGlueClass, (method, annotation) -> {
42-
container.addClass(method.getDeclaringClass());
43-
glueAdaptor.addDefinition(method, annotation);
44-
}));
61+
.collect(Collectors.toSet());
62+
}
63+
64+
private void processClass(Class<?> aGlueClass, GlueAdaptor glueAdaptor) {
65+
scan(aGlueClass, (method, annotation) -> {
66+
container.addClass(method.getDeclaringClass());
67+
glueAdaptor.addDefinition(method, annotation);
68+
});
4569
}
4670

4771
@Override

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

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@
1212
import java.util.ArrayList;
1313
import java.util.Collection;
1414
import java.util.List;
15+
import java.util.Optional;
16+
import java.util.Set;
1517
import java.util.function.Supplier;
18+
import java.util.stream.Collectors;
1619

1720
import static io.cucumber.java8.LambdaGlueRegistry.CLOSED;
1821

@@ -33,20 +36,43 @@ final class Java8Backend implements Backend {
3336

3437
@Override
3538
public void loadGlue(Glue glue, List<URI> gluePaths) {
39+
loadGlueClassesImpl(glue, scanForClasses(gluePaths));
40+
}
41+
42+
@Override
43+
public void loadGlueClasses(Glue glue, Set<String> glueClassNames) {
44+
Set<Class<?>> glueClasses = glueClassNames.stream()
45+
.map(classFinder::safelyLoadClass)
46+
.filter(Optional::isPresent)
47+
.map(Optional::get)
48+
.collect(Collectors.toSet());
49+
50+
loadGlueClassesImpl(glue, glueClasses);
51+
}
52+
53+
private void loadGlueClassesImpl(Glue glue, Set<Class<?>> glueClasses) {
3654
this.glue = new ClosureAwareGlueRegistry(glue);
37-
// Scan for Java8 style glue (lambdas)
38-
gluePaths.stream()
55+
glueClasses.stream()
56+
.filter(aClass -> !LambdaGlue.class.equals(aClass) && LambdaGlue.class.isAssignableFrom(aClass))
57+
.map(aClass -> (Class<? extends LambdaGlue>) aClass.asSubclass(LambdaGlue.class))
58+
.filter(glueClass -> !glueClass.isInterface())
59+
.filter(glueClass -> glueClass.getConstructors().length > 0)
60+
.forEach(this::processClass);
61+
}
62+
63+
private Set<Class<?>> scanForClasses(List<URI> gluePaths) {
64+
return gluePaths.stream()
3965
.filter(gluePath -> ClasspathSupport.CLASSPATH_SCHEME.equals(gluePath.getScheme()))
4066
.map(ClasspathSupport::packageName)
41-
.map(basePackageName -> classFinder.scanForSubClassesInPackage(basePackageName, LambdaGlue.class))
67+
// Scan for Java8 style glue (lambdas)
68+
.map(classFinder::scanForClassesInPackage)
4269
.flatMap(Collection::stream)
43-
.filter(glueClass -> !glueClass.isInterface())
44-
.filter(glueClass -> glueClass.getConstructors().length > 0)
45-
.distinct()
46-
.forEach(glueClass -> {
47-
container.addClass(glueClass);
48-
lambdaGlueClasses.add(glueClass);
49-
});
70+
.collect(Collectors.toSet());
71+
}
72+
73+
private void processClass(Class<? extends LambdaGlue> glueClass) {
74+
container.addClass(glueClass);
75+
lambdaGlueClasses.add(glueClass);
5076
}
5177

5278
@Override

0 commit comments

Comments
 (0)