Skip to content

Commit 06bfbe9

Browse files
committed
Merge branch 'master' of github.com:cucumber/cucumber-jvm
# By Aslak Hellesøy (2) and Björn Rasmusson (1) # Via Aslak Hellesøy * 'master' of github.com:cucumber/cucumber-jvm: Attribution. Closes #581, #584 Remove duplication in Reflections implementations. Make a separate travis job for android
2 parents b7ed998 + be5bff1 commit 06bfbe9

File tree

19 files changed

+185
-176
lines changed

19 files changed

+185
-176
lines changed

.travis.yml

+4-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ jdk:
77
matrix:
88
include:
99
- jdk: oraclejdk7
10-
script: mvn -q deploy -P android --settings .travis-settings.xml -Dno.gem.deploy=true -Dandroid.device=test
10+
script: mvn -q deploy --settings .travis-settings.xml -Dno.gem.deploy=true
11+
- jdk: oraclejdk7
12+
env: ANDROID=true
13+
script: mvn -q deploy -P android -pl android --settings .travis-settings.xml -Dno.gem.deploy=true -Dandroid.device=test
1114
before_install:
1215
# Install base Android SDK
1316
- sudo apt-get update -qq

History.md

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
## [1-1-5-SNAPSHOT (Git master)](https://github.com/cucumber/cucumber-jvm/compare/v1.1.4...master)
22

3+
* [Android] Separate CI job for Android. ([#581](https://github.com/cucumber/cucumber-jvm/issues/581), [#584](https://github.com/cucumber/cucumber-jvm/pull/584) Björn Rasmusson)
34
* [Android] Add support for Dependency Injection via cucumber-picocontainer, cucumber-guice, cucumber-spring etx. (Aslak Hellesøy)
45
* [TestNG] Java Calculator TestNG example project ([#579](https://github.com/cucumber/cucumber-jvm/pull/579) Dmytro Chyzhykov)
56
* [Jython] Access to scenario in Before and After hooks ([#582](https://github.com/cucumber/cucumber-jvm/issues/582) Aslak Hellesøy)

android/src/cucumber/api/android/CucumberInstrumentation.java

+6-6
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212
import cucumber.runtime.android.AndroidFormatter;
1313
import cucumber.runtime.android.AndroidObjectFactory;
1414
import cucumber.runtime.android.AndroidResourceLoader;
15-
import cucumber.runtime.android.DexReflections;
16-
import cucumber.runtime.io.Reflections;
15+
import cucumber.runtime.android.DexClassFinder;
16+
import cucumber.runtime.ClassFinder;
1717
import cucumber.runtime.io.ResourceLoader;
1818
import cucumber.runtime.java.JavaBackend;
1919
import cucumber.runtime.java.ObjectFactory;
@@ -54,10 +54,10 @@ public void onCreate(Bundle arguments) {
5454
classLoader = context.getClassLoader();
5555

5656
String apkPath = context.getPackageCodePath();
57-
Reflections reflections = new DexReflections(newDexFile(apkPath));
57+
ClassFinder classFinder = new DexClassFinder(newDexFile(apkPath));
5858

5959
Class<?> optionsAnnotatedClass = null;
60-
for (Class<?> clazz : reflections.getDescendants(Object.class, context.getPackageName())) {
60+
for (Class<?> clazz : classFinder.getDescendants(Object.class, context.getPackageName())) {
6161
if (clazz.isAnnotationPresent(CucumberOptions.class)) {
6262
Log.d(TAG, "Found CucumberOptions in class " + clazz.getName());
6363
optionsAnnotatedClass = clazz;
@@ -74,9 +74,9 @@ public void onCreate(Bundle arguments) {
7474
resourceLoader = new AndroidResourceLoader(context);
7575

7676
List<Backend> backends = new ArrayList<Backend>();
77-
ObjectFactory delegateObjectFactory = JavaBackend.loadObjectFactory(reflections);
77+
ObjectFactory delegateObjectFactory = JavaBackend.loadObjectFactory(classFinder);
7878
AndroidObjectFactory objectFactory = new AndroidObjectFactory(delegateObjectFactory, this);
79-
backends.add(new JavaBackend(objectFactory, reflections));
79+
backends.add(new JavaBackend(objectFactory, classFinder));
8080
runtime = new Runtime(resourceLoader, classLoader, backends, runtimeOptions);
8181

8282
start();

android/src/cucumber/runtime/android/DexReflections.java renamed to android/src/cucumber/runtime/android/DexClassFinder.java

+4-21
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,22 @@
11
package cucumber.runtime.android;
22

33
import cucumber.runtime.CucumberException;
4-
import cucumber.runtime.io.Reflections;
4+
import cucumber.runtime.ClassFinder;
55
import dalvik.system.DexFile;
66

7-
import java.lang.annotation.Annotation;
87
import java.util.ArrayList;
98
import java.util.Collection;
109
import java.util.Enumeration;
1110
import java.util.List;
1211

13-
public class DexReflections implements Reflections {
14-
private static final ClassLoader CLASS_LOADER = DexReflections.class.getClassLoader();
12+
public class DexClassFinder implements ClassFinder {
13+
private static final ClassLoader CLASS_LOADER = DexClassFinder.class.getClassLoader();
1514
private final DexFile dexFile;
1615

17-
public DexReflections(DexFile dexFile) {
16+
public DexClassFinder(DexFile dexFile) {
1817
this.dexFile = dexFile;
1918
}
2019

21-
@Override
22-
public Collection<Class<? extends Annotation>> getAnnotations(String packageName) {
23-
return getDescendants(Annotation.class, packageName);
24-
}
25-
2620
@Override
2721
public <T> Collection<Class<? extends T>> getDescendants(Class<T> parentType, String packageName) {
2822
List<Class<? extends T>> result = new ArrayList<Class<? extends T>>();
@@ -60,15 +54,4 @@ private boolean isGenerated(String className) {
6054
String shortName = lastDotIndex == -1 ? className : className.substring(lastDotIndex + 1);
6155
return shortName.equals("Manifest") || shortName.equals("R") || shortName.startsWith("R$");
6256
}
63-
64-
65-
@Override
66-
public <T> T instantiateExactlyOneSubclass(Class<T> parentType, String packageName, Class[] constructorParams, Object[] constructorArgs) {
67-
throw new UnsupportedOperationException();
68-
}
69-
70-
@Override
71-
public <T> Collection<? extends T> instantiateSubclasses(Class<T> parentType, String packageName, Class[] constructorParams, Object[] constructorArgs) {
72-
throw new UnsupportedOperationException();
73-
}
7457
}

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

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package cucumber.api.cli;
22

3+
import cucumber.runtime.ClassFinder;
34
import cucumber.runtime.Runtime;
45
import cucumber.runtime.RuntimeOptions;
56
import cucumber.runtime.io.MultiLoader;
7+
import cucumber.runtime.io.ResourceLoader;
8+
import cucumber.runtime.io.ResourceLoaderClassFinder;
69

710
import java.io.IOException;
811

@@ -15,7 +18,9 @@ public static void main(String[] argv) throws Throwable {
1518
public static void run(String[] argv, ClassLoader classLoader) throws IOException {
1619
RuntimeOptions runtimeOptions = new RuntimeOptions(System.getProperties(), argv);
1720

18-
Runtime runtime = new Runtime(new MultiLoader(classLoader), classLoader, runtimeOptions);
21+
ResourceLoader resourceLoader = new MultiLoader(classLoader);
22+
ClassFinder classFinder = new ResourceLoaderClassFinder(resourceLoader, classLoader);
23+
Runtime runtime = new Runtime(resourceLoader, classFinder, classLoader, runtimeOptions);
1924
runtime.writeStepdefsJson();
2025
runtime.run();
2126
System.exit(runtime.exitStatus());
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package cucumber.runtime;
2+
3+
import java.util.Collection;
4+
5+
public interface ClassFinder {
6+
<T> Collection<Class<? extends T>> getDescendants(Class<T> parentType, String packageName);
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package cucumber.runtime;
2+
3+
import java.lang.reflect.InvocationTargetException;
4+
import java.util.Collection;
5+
import java.util.HashSet;
6+
7+
public class Reflections {
8+
private final ClassFinder classFinder;
9+
10+
public Reflections(ClassFinder classFinder) {
11+
this.classFinder = classFinder;
12+
}
13+
14+
public <T> T instantiateExactlyOneSubclass(Class<T> parentType, String packageName, Class[] constructorParams, Object[] constructorArgs) {
15+
Collection<? extends T> instances = instantiateSubclasses(parentType, packageName, constructorParams, constructorArgs);
16+
if (instances.size() == 1) {
17+
return instances.iterator().next();
18+
} else if (instances.size() == 0) {
19+
throw new CucumberException("Couldn't find a single implementation of " + parentType);
20+
} else {
21+
throw new CucumberException("Expected only one instance, but found too many: " + instances);
22+
}
23+
}
24+
25+
public <T> Collection<? extends T> instantiateSubclasses(Class<T> parentType, String packageName, Class[] constructorParams, Object[] constructorArgs) {
26+
Collection<T> result = new HashSet<T>();
27+
for (Class<? extends T> clazz : classFinder.getDescendants(parentType, packageName)) {
28+
if (Utils.isInstantiable(clazz) && hasConstructor(clazz, constructorParams)) {
29+
result.add(newInstance(constructorParams, constructorArgs, clazz));
30+
}
31+
}
32+
return result;
33+
}
34+
35+
public <T> T newInstance(Class[] constructorParams, Object[] constructorArgs, Class<? extends T> clazz) {
36+
try {
37+
return clazz.getConstructor(constructorParams).newInstance(constructorArgs);
38+
} catch (InstantiationException e) {
39+
throw new CucumberException(e);
40+
} catch (IllegalAccessException e) {
41+
throw new CucumberException(e);
42+
} catch (InvocationTargetException e) {
43+
throw new CucumberException(e);
44+
} catch (NoSuchMethodException e) {
45+
throw new CucumberException(e);
46+
}
47+
}
48+
49+
private boolean hasConstructor(Class<?> clazz, Class[] paramTypes) {
50+
try {
51+
clazz.getConstructor(paramTypes);
52+
return true;
53+
} catch (NoSuchMethodException e) {
54+
return false;
55+
}
56+
}
57+
58+
59+
}

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

+5-5
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import cucumber.api.Pending;
44
import cucumber.runtime.io.ResourceLoader;
5-
import cucumber.runtime.io.ResourceLoaderReflections;
65
import cucumber.runtime.model.CucumberFeature;
76
import cucumber.runtime.snippets.SummaryPrinter;
87
import cucumber.runtime.xstream.LocalizedXStreams;
@@ -60,8 +59,8 @@ public class Runtime implements UnreportedStepExecutor {
6059
private boolean skipNextStep = false;
6160
private ScenarioImpl scenarioResult = null;
6261

63-
public Runtime(ResourceLoader resourceLoader, ClassLoader classLoader, RuntimeOptions runtimeOptions) {
64-
this(resourceLoader, classLoader, loadBackends(resourceLoader, classLoader), runtimeOptions);
62+
public Runtime(ResourceLoader resourceLoader, ClassFinder classFinder, ClassLoader classLoader, RuntimeOptions runtimeOptions) {
63+
this(resourceLoader, classLoader, loadBackends(resourceLoader, classFinder), runtimeOptions);
6564
}
6665

6766
public Runtime(ResourceLoader resourceLoader, ClassLoader classLoader, Collection<? extends Backend> backends, RuntimeOptions runtimeOptions) {
@@ -92,8 +91,9 @@ public Runtime(ResourceLoader resourceLoader, ClassLoader classLoader, Collectio
9291
}
9392
}
9493

95-
private static Collection<? extends Backend> loadBackends(ResourceLoader resourceLoader, ClassLoader classLoader) {
96-
return new ResourceLoaderReflections(resourceLoader, classLoader).instantiateSubclasses(Backend.class, "cucumber.runtime", new Class[]{ResourceLoader.class}, new Object[]{resourceLoader});
94+
private static Collection<? extends Backend> loadBackends(ResourceLoader resourceLoader, ClassFinder classFinder) {
95+
Reflections reflections = new Reflections(classFinder);
96+
return reflections.instantiateSubclasses(Backend.class, "cucumber.runtime", new Class[]{ResourceLoader.class}, new Object[]{resourceLoader});
9797
}
9898

9999
public void addError(Throwable error) {

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

+2-9
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import java.net.MalformedURLException;
99
import java.net.URL;
1010
import java.util.ArrayList;
11+
import java.util.Collection;
12+
import java.util.HashSet;
1113
import java.util.List;
1214
import java.util.Map;
1315

@@ -25,15 +27,6 @@ public static boolean isInstantiable(Class<?> clazz) {
2527
return Modifier.isPublic(clazz.getModifiers()) && !Modifier.isAbstract(clazz.getModifiers()) && !isNonStaticInnerClass;
2628
}
2729

28-
public static boolean hasConstructor(Class<?> clazz, Class[] paramTypes) {
29-
try {
30-
clazz.getConstructor(paramTypes);
31-
return true;
32-
} catch (NoSuchMethodException e) {
33-
return false;
34-
}
35-
}
36-
3730
public static Object invoke(final Object target, final Method method, int timeoutMillis, final Object... args) throws Throwable {
3831
return Timeout.timeout(new Timeout.Callback<Object>() {
3932
@Override

core/src/main/java/cucumber/runtime/io/Reflections.java

-14
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package cucumber.runtime.io;
2+
3+
import cucumber.runtime.ClassFinder;
4+
5+
import java.io.File;
6+
import java.util.Collection;
7+
import java.util.HashSet;
8+
9+
public class ResourceLoaderClassFinder implements ClassFinder {
10+
private final ResourceLoader resourceLoader;
11+
private final ClassLoader classLoader;
12+
13+
public ResourceLoaderClassFinder(ResourceLoader resourceLoader, ClassLoader classLoader) {
14+
this.resourceLoader = resourceLoader;
15+
this.classLoader = classLoader;
16+
}
17+
18+
@Override
19+
public <T> Collection<Class<? extends T>> getDescendants(Class<T> parentType, String packageName) {
20+
Collection<Class<? extends T>> result = new HashSet<Class<? extends T>>();
21+
String packagePath = "classpath:" + packageName.replace('.', '/').replace(File.separatorChar, '/');
22+
for (Resource classResource : resourceLoader.resources(packagePath, ".class")) {
23+
String className = classResource.getClassName();
24+
Class<?> clazz = loadClass(className, classLoader);
25+
if (clazz != null && !parentType.equals(clazz) && parentType.isAssignableFrom(clazz)) {
26+
result.add(clazz.asSubclass(parentType));
27+
}
28+
}
29+
return result;
30+
}
31+
32+
private Class<?> loadClass(String className, ClassLoader classLoader) {
33+
try {
34+
return classLoader.loadClass(className);
35+
} catch (ClassNotFoundException ignore) {
36+
return null;
37+
} catch (NoClassDefFoundError ignore) {
38+
return null;
39+
}
40+
}
41+
}

core/src/main/java/cucumber/runtime/io/ResourceLoaderReflections.java

-86
This file was deleted.

0 commit comments

Comments
 (0)