Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 015f73e

Browse files
committed
Load resoureces from classpath when using JUnit, load them from filesystem when using CLI
1 parent 125ea92 commit 015f73e

File tree

21 files changed

+168
-135
lines changed

21 files changed

+168
-135
lines changed

clojure/src/main/java/cucumber/runtime/clojure/ClojureBackend.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,11 @@
1515

1616
public class ClojureBackend implements Backend {
1717
private static ClojureBackend instance;
18-
private final ResourceLoader resourceLoader = new ResourceLoader();
18+
private final ResourceLoader resourceLoader;
1919
private World world;
2020

21-
public ClojureBackend() {
21+
public ClojureBackend(ResourceLoader resourceLoader) {
22+
this.resourceLoader = resourceLoader;
2223
instance = this;
2324
loadScript("cucumber/runtime/clojure/dsl");
2425
}
@@ -27,7 +28,7 @@ public ClojureBackend() {
2728
public void buildWorld(List<String> gluePaths, World world) {
2829
this.world = world;
2930
for (String gluePath : gluePaths) {
30-
for (Resource resource : resourceLoader.fileResources(gluePath, ".clj")) {
31+
for (Resource resource : resourceLoader.resources(gluePath, ".clj")) {
3132
loadScript(resource.getPath());
3233
}
3334
}

core/src/main/java/cucumber/cli/Main.java

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

33
import cucumber.formatter.FormatterFactory;
4+
import cucumber.io.FileResourceLoader;
45
import cucumber.runtime.Runtime;
56
import cucumber.runtime.snippets.SummaryPrinter;
67
import gherkin.formatter.Formatter;
@@ -9,7 +10,6 @@
910
import java.io.IOException;
1011
import java.util.ArrayList;
1112
import java.util.List;
12-
import java.util.regex.Pattern;
1313

1414
import static java.util.Arrays.asList;
1515

@@ -56,7 +56,7 @@ public static void main(String[] argv) throws Throwable {
5656
System.exit(1);
5757
}
5858

59-
Runtime runtime = new Runtime(gluePaths, isDryRun);
59+
Runtime runtime = new Runtime(gluePaths, new FileResourceLoader(), isDryRun);
6060

6161
if (dotCucumber != null) {
6262
writeDotCucumber(featurePaths, dotCucumber, runtime);
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package cucumber.io;
2+
3+
import cucumber.runtime.CucumberException;
4+
import cucumber.runtime.Utils;
5+
6+
import java.lang.annotation.Annotation;
7+
import java.lang.reflect.InvocationTargetException;
8+
import java.util.Collection;
9+
import java.util.HashSet;
10+
11+
public class ClasspathResourceLoader implements ResourceLoader {
12+
@Override
13+
public Iterable<Resource> resources(String path, String suffix) {
14+
return new ClasspathIterable(cl(), path, suffix);
15+
}
16+
17+
public Collection<Class<? extends Annotation>> getAnnotations(String packagePath) {
18+
return getDescendants(Annotation.class, packagePath);
19+
}
20+
21+
public <T> Collection<Class<? extends T>> getDescendants(Class<T> parentType, String packagePath) {
22+
Collection<Class<? extends T>> result = new HashSet<Class<? extends T>>();
23+
for (Resource classResource : resources(packagePath, ".class")) {
24+
String className = className(classResource.getPath());
25+
Class<?> clazz = loadClass(className);
26+
if (clazz != null && !parentType.equals(clazz) && parentType.isAssignableFrom(clazz)) {
27+
result.add(clazz.asSubclass(parentType));
28+
}
29+
}
30+
return result;
31+
}
32+
33+
public <T> T instantiateExactlyOneSubclass(Class<T> parentType, String packagePath, Class[] constructorParams, Object[] constructorArgs) {
34+
Collection<? extends T> instances = instantiateSubclasses(parentType, packagePath, constructorParams, constructorArgs);
35+
if (instances.size() == 1) {
36+
return instances.iterator().next();
37+
} else if (instances.size() == 0) {
38+
throw new CucumberException("Couldn't find a single implementation of " + parentType);
39+
} else {
40+
throw new CucumberException("Expected only one instance, but found too many: " + instances);
41+
}
42+
}
43+
44+
public <T> Collection<? extends T> instantiateSubclasses(Class<T> parentType, String packagePath, Class[] constructorParams, Object[] constructorArgs) {
45+
Collection<T> result = new HashSet<T>();
46+
for (Class<? extends T> clazz : getDescendants(parentType, packagePath)) {
47+
if (Utils.isInstantiable(clazz)) {
48+
result.add(newInstance(constructorParams, constructorArgs, clazz));
49+
}
50+
}
51+
return result;
52+
}
53+
54+
private Class<?> loadClass(String className) {
55+
try {
56+
return cl().loadClass(className);
57+
} catch (ClassNotFoundException ignore) {
58+
return null;
59+
} catch (NoClassDefFoundError ignore) {
60+
return null;
61+
}
62+
}
63+
64+
private <T> T newInstance(Class[] constructorParams, Object[] constructorArgs, Class<? extends T> clazz) {
65+
try {
66+
return clazz.getConstructor(constructorParams).newInstance(constructorArgs);
67+
} catch (InstantiationException e) {
68+
throw new CucumberException(e);
69+
} catch (IllegalAccessException e) {
70+
throw new CucumberException(e);
71+
} catch (InvocationTargetException e) {
72+
throw new CucumberException(e);
73+
} catch (NoSuchMethodException e) {
74+
throw new CucumberException(e);
75+
}
76+
}
77+
78+
private String className(String pathToClass) {
79+
return pathToClass.substring(0, pathToClass.length() - 6).replace("/", ".");
80+
}
81+
82+
private ClassLoader cl() {
83+
return Thread.currentThread().getContextClassLoader();
84+
}
85+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package cucumber.io;
2+
3+
import java.io.File;
4+
5+
public class FileResourceLoader implements ResourceLoader {
6+
@Override
7+
public Iterable<Resource> resources(String path, String suffix) {
8+
File root = new File(path);
9+
return new FileResourceIterable(root, root, suffix);
10+
}
11+
}
Lines changed: 2 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,5 @@
11
package cucumber.io;
22

3-
import cucumber.runtime.CucumberException;
4-
import cucumber.runtime.Utils;
5-
6-
import java.io.File;
7-
import java.lang.annotation.Annotation;
8-
import java.lang.reflect.InvocationTargetException;
9-
import java.util.Collection;
10-
import java.util.HashSet;
11-
12-
public class ResourceLoader {
13-
public Iterable<Resource> fileResources(String path, String suffix) {
14-
File root = new File(path);
15-
return new FileResourceIterable(root, root, suffix);
16-
}
17-
18-
public Iterable<Resource> classpathResources(String path, String suffix) {
19-
return new ClasspathIterable(cl(), path, suffix);
20-
}
21-
22-
public Collection<Class<? extends Annotation>> getAnnotations(String packagePath) {
23-
return getDescendants(Annotation.class, packagePath);
24-
}
25-
26-
public <T> T instantiateExactlyOneSubclass(Class<T> parentType, String packagePath, Class[] constructorParams, Object[] constructorArgs) {
27-
Collection<? extends T> instances = instantiateSubclasses(parentType, packagePath, constructorParams, constructorArgs);
28-
if (instances.size() == 1) {
29-
return instances.iterator().next();
30-
} else if (instances.size() == 0) {
31-
throw new CucumberException("Couldn't find a single implementation of " + parentType);
32-
} else {
33-
throw new CucumberException("Expected only one instance, but found too many: " + instances);
34-
}
35-
}
36-
37-
public <T> Collection<? extends T> instantiateSubclasses(Class<T> parentType, String packagePath, Class[] constructorParams, Object[] constructorArgs) {
38-
Collection<T> result = new HashSet<T>();
39-
for (Class<? extends T> clazz : getDescendants(parentType, packagePath)) {
40-
if(Utils.isInstantiable(clazz)) {
41-
result.add(newInstance(constructorParams, constructorArgs, clazz));
42-
}
43-
}
44-
return result;
45-
}
46-
47-
public <T> Collection<Class<? extends T>> getDescendants(Class<T> parentType, String packagePath) {
48-
Collection<Class<? extends T>> result = new HashSet<Class<? extends T>>();
49-
for (Resource classResource : classpathResources(packagePath, ".class")) {
50-
String className = className(classResource.getPath());
51-
Class<?> clazz = loadClass(className);
52-
if (clazz != null && !parentType.equals(clazz) && parentType.isAssignableFrom(clazz)) {
53-
result.add(clazz.asSubclass(parentType));
54-
}
55-
}
56-
return result;
57-
}
58-
59-
private Class<?> loadClass(String className) {
60-
try {
61-
return cl().loadClass(className);
62-
} catch (ClassNotFoundException ignore) {
63-
return null;
64-
} catch (NoClassDefFoundError ignore) {
65-
return null;
66-
}
67-
}
68-
69-
private <T> T newInstance(Class[] constructorParams, Object[] constructorArgs, Class<? extends T> clazz) {
70-
try {
71-
return clazz.getConstructor(constructorParams).newInstance(constructorArgs);
72-
} catch (InstantiationException e) {
73-
throw new CucumberException(e);
74-
} catch (IllegalAccessException e) {
75-
throw new CucumberException(e);
76-
} catch (InvocationTargetException e) {
77-
throw new CucumberException(e);
78-
} catch (NoSuchMethodException e) {
79-
throw new CucumberException(e);
80-
}
81-
}
82-
83-
private String className(String pathToClass) {
84-
return pathToClass.substring(0, pathToClass.length() - 6).replace("/", ".");
85-
}
86-
87-
private ClassLoader cl() {
88-
return Thread.currentThread().getContextClassLoader();
89-
}
3+
public interface ResourceLoader {
4+
Iterable<Resource> resources(String path, String suffix);
905
}

core/src/main/java/cucumber/runtime/Runtime.java

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.google.gson.Gson;
44
import com.google.gson.GsonBuilder;
5+
import cucumber.io.ClasspathResourceLoader;
56
import cucumber.io.ResourceLoader;
67
import cucumber.runtime.autocomplete.MetaStepdef;
78
import cucumber.runtime.autocomplete.StepdefGenerator;
@@ -19,7 +20,7 @@
1920
import java.util.List;
2021
import java.util.Locale;
2122

22-
import static cucumber.runtime.model.CucumberFeature.loadFromClasspath;
23+
import static cucumber.runtime.model.CucumberFeature.load;
2324
import static java.util.Collections.emptyList;
2425

2526
/**
@@ -35,31 +36,34 @@ public class Runtime {
3536
private final Collection<? extends Backend> backends;
3637
private final boolean isDryRun;
3738
private final List<String> gluePaths;
39+
private final ResourceLoader resourceLoader;
3840

39-
public Runtime(List<String> gluePaths) {
40-
this(gluePaths, false);
41+
public Runtime(List<String> gluePaths, ResourceLoader resourceLoader) {
42+
this(gluePaths, resourceLoader, false);
4143
}
4244

43-
public Runtime(List<String> gluePaths, boolean isDryRun) {
44-
this.gluePaths = gluePaths;
45-
this.backends = new ResourceLoader().instantiateSubclasses(Backend.class, "cucumber/runtime", new Class[0], new Object[0]);
46-
this.isDryRun = isDryRun;
47-
this.tracker = new UndefinedStepsTracker(backends);
45+
public Runtime(List<String> gluePaths, ResourceLoader resourceLoader, boolean isDryRun) {
46+
this(gluePaths, resourceLoader, loadBackends(resourceLoader), isDryRun);
4847
}
4948

50-
public Runtime(List<String> gluePaths, List<? extends Backend> backends, boolean isDryRun) {
49+
public Runtime(List<String> gluePaths, ResourceLoader resourceLoader, Collection<? extends Backend> backends, boolean isDryRun) {
5150
this.gluePaths = gluePaths;
5251
this.backends = backends;
52+
this.resourceLoader = resourceLoader;
5353
this.isDryRun = isDryRun;
5454
this.tracker = new UndefinedStepsTracker(backends);
5555
}
5656

57+
private static Collection<? extends Backend> loadBackends(ResourceLoader resourceLoader) {
58+
return new ClasspathResourceLoader().instantiateSubclasses(Backend.class, "cucumber/runtime", new Class[]{ResourceLoader.class}, new Object[]{resourceLoader});
59+
}
60+
5761
public void addError(Throwable error) {
5862
errors.add(error);
5963
}
6064

6165
public void run(List<String> featurePaths, final List<Object> filters, gherkin.formatter.Formatter formatter, Reporter reporter) {
62-
for (CucumberFeature cucumberFeature : loadFromClasspath(featurePaths, filters)) {
66+
for (CucumberFeature cucumberFeature : load(resourceLoader, featurePaths, filters)) {
6367
run(cucumberFeature, formatter, reporter);
6468
}
6569
}
@@ -87,7 +91,7 @@ public void disposeBackendWorlds() {
8791
}
8892

8993
public void writeStepdefsJson(List<String> featurePaths, File dotCucumber) throws IOException {
90-
List<CucumberFeature> features = loadFromClasspath(featurePaths, NO_FILTERS);
94+
List<CucumberFeature> features = load(resourceLoader, featurePaths, NO_FILTERS);
9195
World world = new RuntimeWorld(this, NO_TAGS);
9296
buildBackendWorlds(world);
9397
List<StepDefinition> stepDefs = world.getStepDefinitions();

core/src/main/java/cucumber/runtime/model/CucumberFeature.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,11 @@ public class CucumberFeature {
2424
private CucumberScenarioOutline currentScenarioOutline;
2525

2626
// TODO: Use an iterable here, wrapping around
27-
public static List<CucumberFeature> loadFromClasspath(List<String> featurePaths, final List<Object> filters) {
28-
ResourceLoader resourceLoader = new ResourceLoader();
27+
public static List<CucumberFeature> load(ResourceLoader resourceLoader, List<String> featurePaths, final List<Object> filters) {
2928
final List<CucumberFeature> cucumberFeatures = new ArrayList<CucumberFeature>();
3029
final FeatureBuilder builder = new FeatureBuilder(cucumberFeatures);
3130
for (String featurePath : featurePaths) {
32-
Iterable<Resource> resources = resourceLoader.classpathResources(featurePath, ".feature");
31+
Iterable<Resource> resources = resourceLoader.resources(featurePath, ".feature");
3332
for (Resource resource : resources) {
3433
builder.parse(resource, filters);
3534
}

core/src/test/java/cucumber/io/ResourceLoaderTest.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,24 +11,23 @@
1111

1212
public class ResourceLoaderTest {
1313
private final File dir = new File(getClass().getProtectionDomain().getCodeSource().getLocation().getFile());
14-
private ResourceLoader l = new ResourceLoader();
1514

1615
@Test
1716
public void loads_resources_from_filesystem_dir() {
18-
Iterable<Resource> files = l.fileResources(dir.getAbsolutePath(), ".properties");
17+
Iterable<Resource> files = new FileResourceLoader().resources(dir.getAbsolutePath(), ".properties");
1918
assertEquals(3, toList(files).size());
2019
}
2120

2221
@Test
2322
public void loads_resource_from_filesystem_file() {
2423
File file = new File(dir, "cucumber/runtime/bar.properties");
25-
Iterable<Resource> files = l.fileResources(file.getPath(), ".doesntmatter");
24+
Iterable<Resource> files = new FileResourceLoader().resources(file.getPath(), ".doesntmatter");
2625
assertEquals(1, toList(files).size());
2726
}
2827

2928
@Test
3029
public void loads_resources_from_jar_on_classpath() throws IOException {
31-
Iterable<Resource> files = l.classpathResources("cucumber", ".properties");
30+
Iterable<Resource> files = new ClasspathResourceLoader().resources("cucumber", ".properties");
3231
assertEquals(3, toList(files).size());
3332
}
3433

core/src/test/java/cucumber/runtime/BackgroundTest.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package cucumber.runtime;
22

3+
import cucumber.io.ClasspathResourceLoader;
34
import cucumber.runtime.model.CucumberFeature;
45
import gherkin.formatter.PrettyFormatter;
56
import gherkin.formatter.model.Step;
@@ -17,7 +18,7 @@ public class BackgroundTest {
1718
@Test
1819
public void should_run_background() throws IOException {
1920
Backend backend = new TestBackend();
20-
Runtime runtime = new Runtime(new ArrayList<String>(), asList(backend), false);
21+
Runtime runtime = new Runtime(new ArrayList<String>(), new ClasspathResourceLoader(), asList(backend), false);
2122
CucumberFeature feature = feature("test.feature", "" +
2223
"Feature:\n" +
2324
" Background:\n" +
@@ -41,7 +42,7 @@ public void should_run_background() throws IOException {
4142

4243
// TODO: Add some negative tests to verify how it behaves with failure
4344

44-
public static class TestBackend implements Backend {
45+
private class TestBackend implements Backend {
4546
@Override
4647
public void buildWorld(List<String> gluePaths, World world) {
4748
}

core/src/test/java/cucumber/runtime/HookTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package cucumber.runtime;
22

3+
import cucumber.io.ClasspathResourceLoader;
34
import gherkin.formatter.Reporter;
45
import org.junit.Test;
56
import org.mockito.InOrder;
@@ -28,7 +29,7 @@ public void after_hooks_execute_before_objects_are_disposed() throws Throwable {
2829
HookDefinition hook = mock(HookDefinition.class);
2930
when(hook.matches(anyListOf(String.class))).thenReturn(true);
3031

31-
Runtime runtime = new Runtime(CODE_PATHS, asList(backend), false);
32+
Runtime runtime = new Runtime(CODE_PATHS, new ClasspathResourceLoader(), asList(backend), false);
3233
World world = new RuntimeWorld(runtime, TAGS);
3334
world.addAfterHook(hook);
3435

0 commit comments

Comments
 (0)