From 43635cb39159d85768f7a2d24d402848438ee18f Mon Sep 17 00:00:00 2001 From: "M.P. Korstanje" Date: Sun, 9 Jul 2017 23:02:48 +0200 Subject: [PATCH] [Java8] Replace ConstantPoolTypeIntrospector with TypeResolver To determine which argument types a lambda function requires we created a ConstantPoolTypeIntrospector. However it has never functioned quite correctly (#937, #1140, #957), was prone to breaking (#912, #914) and hasn't been tested much (#1048). It is important to understand that while we will get a properly functioning and tested replacement, TypeResolver uses the same ConstantPool and thus has the same potential to break. However because TypeResolver is used by a much larger audience I expect these problems to be shallow. Because this change the interface of Java8StepDefinition it made sense to refactor all the Java8 related stuff out of cucumber-java. This will make it easier in the future to add things like KotlinStepDefintions without creating a separate KotlinBackend. Related issues: - #912 - #914 - #937 - #957 - #1140 - #1048 - #1140 --- java/pom.xml | 10 -- .../cucumber/runtime/java/JavaBackend.java | 66 +++++------- .../cucumber/runtime/java/MethodScanner.java | 6 +- .../runtime/java/ObjectFactoryLoader.java | 2 +- .../runtime/java/TypeIntrospector.java | 9 -- .../cucumber/runtime/java/JavaHookTest.java | 1 - .../java8test/AnonInnerClassStepdefs.java | 20 ---- .../runtime/java/java8test/RunCukesTest.java | 10 -- java8/pom.xml | 4 + java8/src/main/code_generator/I18n.java8.txt | 21 ++-- .../java/cucumber/api/java8/HookBody.java | 0 .../cucumber/api/java8/HookNoArgsBody.java | 0 .../java/cucumber/api/java8/StepdefBody.java | 0 .../java8/ConstantPoolTypeIntrospector.java | 102 ------------------ .../runtime/java8}/Java8HookDefinition.java | 25 ++--- .../runtime/java8}/Java8StepDefinition.java | 44 ++++---- .../runtime/java8/LambdaGlueBase.java | 40 +++---- ...Java8AnonInnerClassStepDefinitionTest.java | 35 +++--- .../java8/Java8LambdaStepDefinitionTest.java | 50 +++++++++ .../java8/test/AnonInnerClassStepdefs.java | 24 +++++ .../java8/test/Java8StepDefinitionTest.java | 56 ---------- .../runtime/java8/test/LambdaStepdefs.java | 8 +- .../anon-inner-class-step-definitions.feature | 2 +- ...eature => lambda-step-definitions.feature} | 1 + pom.xml | 6 ++ 25 files changed, 206 insertions(+), 336 deletions(-) delete mode 100644 java/src/main/java/cucumber/runtime/java/TypeIntrospector.java delete mode 100644 java/src/test/java/cucumber/runtime/java/java8test/AnonInnerClassStepdefs.java delete mode 100644 java/src/test/java/cucumber/runtime/java/java8test/RunCukesTest.java rename {java => java8}/src/main/java/cucumber/api/java8/HookBody.java (100%) rename {java => java8}/src/main/java/cucumber/api/java8/HookNoArgsBody.java (100%) rename {java => java8}/src/main/java/cucumber/api/java8/StepdefBody.java (100%) delete mode 100644 java8/src/main/java/cucumber/runtime/java8/ConstantPoolTypeIntrospector.java rename {java/src/main/java/cucumber/runtime/java => java8/src/main/java/cucumber/runtime/java8}/Java8HookDefinition.java (84%) rename {java/src/main/java/cucumber/runtime/java => java8/src/main/java/cucumber/runtime/java8}/Java8StepDefinition.java (68%) rename java/src/test/java/cucumber/runtime/java/Java8StepDefinitionTest.java => java8/src/test/java/cucumber/runtime/java8/Java8AnonInnerClassStepDefinitionTest.java (66%) create mode 100644 java8/src/test/java/cucumber/runtime/java8/Java8LambdaStepDefinitionTest.java create mode 100644 java8/src/test/java/cucumber/runtime/java8/test/AnonInnerClassStepdefs.java delete mode 100644 java8/src/test/java/cucumber/runtime/java8/test/Java8StepDefinitionTest.java rename java/src/test/resources/cucumber/runtime/java/java8test/java8.feature => java8/src/test/resources/cucumber/runtime/java8/test/anon-inner-class-step-definitions.feature (56%) rename java8/src/test/resources/cucumber/runtime/java8/test/{java8.feature => lambda-step-definitions.feature} (95%) diff --git a/java/pom.xml b/java/pom.xml index b2abcae776..c8acf99b45 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -17,16 +17,6 @@ io.cucumber cucumber-core - - info.cukes - cucumber-jvm-deps - provided - - - io.cucumber - gherkin - provided - io.cucumber diff --git a/java/src/main/java/cucumber/runtime/java/JavaBackend.java b/java/src/main/java/cucumber/runtime/java/JavaBackend.java index c23e942956..9a781d32de 100644 --- a/java/src/main/java/cucumber/runtime/java/JavaBackend.java +++ b/java/src/main/java/cucumber/runtime/java/JavaBackend.java @@ -1,18 +1,21 @@ package cucumber.runtime.java; +import static cucumber.runtime.io.MultiLoader.packageName; +import static cucumber.runtime.java.ObjectFactoryLoader.loadObjectFactory; +import static java.lang.Thread.currentThread; + import cucumber.api.java.After; import cucumber.api.java.Before; import cucumber.api.java.ObjectFactory; import cucumber.api.java8.GlueBase; -import cucumber.api.java8.HookBody; -import cucumber.api.java8.HookNoArgsBody; -import cucumber.api.java8.StepdefBody; import cucumber.runtime.Backend; import cucumber.runtime.ClassFinder; import cucumber.runtime.CucumberException; import cucumber.runtime.DuplicateStepDefinitionException; import cucumber.runtime.Env; import cucumber.runtime.Glue; +import cucumber.runtime.HookDefinition; +import cucumber.runtime.StepDefinition; import cucumber.runtime.UnreportedStepExecutor; import cucumber.runtime.Utils; import cucumber.runtime.io.MultiLoader; @@ -30,14 +33,12 @@ import java.util.List; import java.util.regex.Pattern; -import static cucumber.runtime.io.MultiLoader.packageName; - public class JavaBackend implements Backend { public static final ThreadLocal INSTANCE = new ThreadLocal(); private final SnippetGenerator snippetGenerator = new SnippetGenerator(createSnippet()); private Snippet createSnippet() { - ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + ClassLoader classLoader = currentThread().getContextClassLoader(); try { classLoader.loadClass("cucumber.runtime.java8.LambdaGlueBase"); return new Java8Snippet(); @@ -59,24 +60,25 @@ private Snippet createSnippet() { * @param resourceLoader */ public JavaBackend(ResourceLoader resourceLoader) { - ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); - classFinder = new ResourceLoaderClassFinder(resourceLoader, classLoader); - methodScanner = new MethodScanner(classFinder); - objectFactory = ObjectFactoryLoader.loadObjectFactory(classFinder, Env.INSTANCE.get(ObjectFactory.class.getName())); + this(new ResourceLoaderClassFinder(resourceLoader, currentThread().getContextClassLoader())); + } + + private JavaBackend(ClassFinder classFinder) { + this(loadObjectFactory(classFinder, Env.INSTANCE.get(ObjectFactory.class.getName())), classFinder); } public JavaBackend(ObjectFactory objectFactory) { - ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); - ResourceLoader resourceLoader = new MultiLoader(classLoader); - classFinder = new ResourceLoaderClassFinder(resourceLoader, classLoader); - methodScanner = new MethodScanner(classFinder); - this.objectFactory = objectFactory; + this(objectFactory, + new ResourceLoaderClassFinder( + new MultiLoader( + currentThread().getContextClassLoader()), + currentThread().getContextClassLoader())); } public JavaBackend(ObjectFactory objectFactory, ClassFinder classFinder) { - this.objectFactory = objectFactory; this.classFinder = classFinder; - methodScanner = new MethodScanner(classFinder); + this.objectFactory = objectFactory; + this.methodScanner = new MethodScanner(classFinder); } @Override @@ -157,14 +159,8 @@ void addStepDefinition(Annotation annotation, Method method) { } } - public void addStepDefinition(String regexp, long timeoutMillis, StepdefBody body, TypeIntrospector typeIntrospector) { - try { - glue.addStepDefinition(new Java8StepDefinition(Pattern.compile(regexp), timeoutMillis, body, typeIntrospector)); - } catch (CucumberException e) { - throw e; - } catch (Exception e) { - throw new CucumberException(e); - } + public void addStepDefinition(StepDefinition stepDefinition) { + glue.addStepDefinition(stepDefinition); } void addHook(Annotation annotation, Method method) { @@ -172,29 +168,21 @@ void addHook(Annotation annotation, Method method) { if (annotation.annotationType().equals(Before.class)) { String[] tagExpressions = ((Before) annotation).value(); long timeout = ((Before) annotation).timeout(); - glue.addBeforeHook(new JavaHookDefinition(method, tagExpressions, ((Before) annotation).order(), timeout, objectFactory)); + addBeforeHookDefinition(new JavaHookDefinition(method, tagExpressions, ((Before) annotation).order(), timeout, objectFactory)); } else { String[] tagExpressions = ((After) annotation).value(); long timeout = ((After) annotation).timeout(); - glue.addAfterHook(new JavaHookDefinition(method, tagExpressions, ((After) annotation).order(), timeout, objectFactory)); + addAfterHookDefinition(new JavaHookDefinition(method, tagExpressions, ((After) annotation).order(), timeout, objectFactory)); } } } - public void addBeforeHookDefinition(String[] tagExpressions, long timeoutMillis, int order, HookBody body) { - glue.addBeforeHook(new Java8HookDefinition(tagExpressions, order, timeoutMillis, body)); - } - - public void addAfterHookDefinition(String[] tagExpressions, long timeoutMillis, int order, HookBody body) { - glue.addAfterHook(new Java8HookDefinition(tagExpressions, order, timeoutMillis, body)); - } - - public void addBeforeHookDefinition(String[] tagExpressions, long timeoutMillis, int order, HookNoArgsBody body) { - glue.addBeforeHook(new Java8HookDefinition(tagExpressions, order, timeoutMillis, body)); + public void addBeforeHookDefinition(HookDefinition beforeHook) { + glue.addBeforeHook(beforeHook); } - public void addAfterHookDefinition(String[] tagExpressions, long timeoutMillis, int order, HookNoArgsBody body) { - glue.addAfterHook(new Java8HookDefinition(tagExpressions, order, timeoutMillis, body)); + public void addAfterHookDefinition(HookDefinition afterHook) { + glue.addAfterHook(afterHook); } private Pattern pattern(Annotation annotation) throws Throwable { diff --git a/java/src/main/java/cucumber/runtime/java/MethodScanner.java b/java/src/main/java/cucumber/runtime/java/MethodScanner.java index bc701a5ab8..db542385dc 100644 --- a/java/src/main/java/cucumber/runtime/java/MethodScanner.java +++ b/java/src/main/java/cucumber/runtime/java/MethodScanner.java @@ -18,7 +18,7 @@ class MethodScanner { private final ClassFinder classFinder; - public MethodScanner(ClassFinder classFinder) { + MethodScanner(ClassFinder classFinder) { this.classFinder = classFinder; cucumberAnnotationClasses = findCucumberAnnotationClasses(); } @@ -29,7 +29,7 @@ public MethodScanner(ClassFinder classFinder) { * @param javaBackend the backend where stepdefs and hooks will be registered * @param gluePaths where to look */ - public void scan(JavaBackend javaBackend, List gluePaths) { + void scan(JavaBackend javaBackend, List gluePaths) { for (String gluePath : gluePaths) { for (Class glueCodeClass : classFinder.getDescendants(Object.class, packageName(gluePath))) { while (glueCodeClass != null && glueCodeClass != Object.class && !Utils.isInstantiable(glueCodeClass)) { @@ -55,7 +55,7 @@ public void scan(JavaBackend javaBackend, List gluePaths) { * @param method a candidate for being a stepdef or hook. * @param glueCodeClass the class where the method is declared. */ - public void scan(JavaBackend javaBackend, Method method, Class glueCodeClass) { + void scan(JavaBackend javaBackend, Method method, Class glueCodeClass) { for (Class cucumberAnnotationClass : cucumberAnnotationClasses) { Annotation annotation = method.getAnnotation(cucumberAnnotationClass); if (annotation != null) { diff --git a/java/src/main/java/cucumber/runtime/java/ObjectFactoryLoader.java b/java/src/main/java/cucumber/runtime/java/ObjectFactoryLoader.java index 5b7bd3ff90..8c28ea2e7d 100644 --- a/java/src/main/java/cucumber/runtime/java/ObjectFactoryLoader.java +++ b/java/src/main/java/cucumber/runtime/java/ObjectFactoryLoader.java @@ -7,7 +7,7 @@ import cucumber.runtime.Reflections; import cucumber.runtime.TooManyInstancesException; -public class ObjectFactoryLoader { +public final class ObjectFactoryLoader { private ObjectFactoryLoader() { } diff --git a/java/src/main/java/cucumber/runtime/java/TypeIntrospector.java b/java/src/main/java/cucumber/runtime/java/TypeIntrospector.java deleted file mode 100644 index 549cf25965..0000000000 --- a/java/src/main/java/cucumber/runtime/java/TypeIntrospector.java +++ /dev/null @@ -1,9 +0,0 @@ -package cucumber.runtime.java; - -import cucumber.api.java8.StepdefBody; - -import java.lang.reflect.Type; - -public interface TypeIntrospector { - Type[] getGenericTypes(Class clazz, Class interfac3) throws Exception; -} diff --git a/java/src/test/java/cucumber/runtime/java/JavaHookTest.java b/java/src/test/java/cucumber/runtime/java/JavaHookTest.java index 64c7d09b76..136b9e682e 100644 --- a/java/src/test/java/cucumber/runtime/java/JavaHookTest.java +++ b/java/src/test/java/cucumber/runtime/java/JavaHookTest.java @@ -7,7 +7,6 @@ import cucumber.runtime.Glue; import cucumber.runtime.HookDefinition; import cucumber.runtime.RuntimeGlue; -import cucumber.runtime.UndefinedStepsTracker; import cucumber.runtime.xstream.LocalizedXStreams; import gherkin.pickles.PickleLocation; import gherkin.pickles.PickleTag; diff --git a/java/src/test/java/cucumber/runtime/java/java8test/AnonInnerClassStepdefs.java b/java/src/test/java/cucumber/runtime/java/java8test/AnonInnerClassStepdefs.java deleted file mode 100644 index f803b9b236..0000000000 --- a/java/src/test/java/cucumber/runtime/java/java8test/AnonInnerClassStepdefs.java +++ /dev/null @@ -1,20 +0,0 @@ -package cucumber.runtime.java.java8test; - -import cucumber.api.java8.GlueBase; -import cucumber.api.java8.StepdefBody; -import cucumber.runtime.java.JavaBackend; - -import static org.junit.Assert.assertEquals; - -public class AnonInnerClassStepdefs implements GlueBase { - - public AnonInnerClassStepdefs() { - JavaBackend.INSTANCE.get().addStepDefinition("^I have (\\d+) cukes in my (.*)", 0, new StepdefBody.A2() { - @Override - public void accept(Integer cukes, String what) throws Throwable { - assertEquals(42, cukes.intValue()); - assertEquals("belly", what); - } - }, null); - } -} diff --git a/java/src/test/java/cucumber/runtime/java/java8test/RunCukesTest.java b/java/src/test/java/cucumber/runtime/java/java8test/RunCukesTest.java deleted file mode 100644 index 2f989726a2..0000000000 --- a/java/src/test/java/cucumber/runtime/java/java8test/RunCukesTest.java +++ /dev/null @@ -1,10 +0,0 @@ -package cucumber.runtime.java.java8test; - -import cucumber.api.CucumberOptions; -import cucumber.api.junit.Cucumber; -import org.junit.runner.RunWith; - -@RunWith(Cucumber.class) -@CucumberOptions(strict = true) -public class RunCukesTest { -} diff --git a/java8/pom.xml b/java8/pom.xml index 88f6b37a20..28af78385d 100644 --- a/java8/pom.xml +++ b/java8/pom.xml @@ -17,6 +17,10 @@ io.cucumber cucumber-java + + net.jodah + typetools + io.cucumber diff --git a/java8/src/main/code_generator/I18n.java8.txt b/java8/src/main/code_generator/I18n.java8.txt index b67377b9db..4bb094fc41 100644 --- a/java8/src/main/code_generator/I18n.java8.txt +++ b/java8/src/main/code_generator/I18n.java8.txt @@ -1,28 +1,37 @@ package cucumber.api.java8; -import cucumber.runtime.java8.ConstantPoolTypeIntrospector; -import cucumber.runtime.java8.LambdaGlueBase; import cucumber.runtime.java.JavaBackend; +import cucumber.runtime.java8.Java8StepDefinition; +import cucumber.runtime.java8.LambdaGlueBase; public interface ${className} extends LambdaGlueBase { <% i18n.stepKeywords.findAll { !it.contains('*') && !it.matches("^\\d.*") }.sort().unique().each { kw -> %> default void ${java.text.Normalizer.normalize(kw.replaceAll("[\\s',!]", ""), java.text.Normalizer.Form.NFC)}(final String regexp, final StepdefBody.A0 body) { - JavaBackend.INSTANCE.get().addStepDefinition(regexp, 0, body, ConstantPoolTypeIntrospector.INSTANCE); + JavaBackend.INSTANCE.get().addStepDefinition( + new Java8StepDefinition(regexp, 0, StepdefBody.A0.class, body) + ); } default void ${java.text.Normalizer.normalize(kw.replaceAll("[\\s',!]", ""), java.text.Normalizer.Form.NFC)}(final String regexp, final long timeoutMillis, final StepdefBody.A0 body) { - JavaBackend.INSTANCE.get().addStepDefinition(regexp, timeoutMillis, body, ConstantPoolTypeIntrospector.INSTANCE); + JavaBackend.INSTANCE.get().addStepDefinition( + new Java8StepDefinition(regexp, timeoutMillis, StepdefBody.A0.class, body) + ); } <% (1..9).each { arity -> def ts = (1..arity).collect { n -> "T"+n } def genericSignature = ts.join(",") %> default <${genericSignature}> void ${java.text.Normalizer.normalize(kw.replaceAll("[\\s',!]", ""), java.text.Normalizer.Form.NFC)}(final String regexp, final StepdefBody.A${arity}<${genericSignature}> body) { - JavaBackend.INSTANCE.get().addStepDefinition(regexp, 0, body, ConstantPoolTypeIntrospector.INSTANCE); + JavaBackend.INSTANCE.get().addStepDefinition( + new Java8StepDefinition(regexp, 0, StepdefBody.A${arity}.class, body) + ); + } default <${genericSignature}> void ${java.text.Normalizer.normalize(kw.replaceAll("[\\s',!]", ""), java.text.Normalizer.Form.NFC)}(final String regexp, final long timeoutMillis, final StepdefBody.A${arity}<${genericSignature}> body) { - JavaBackend.INSTANCE.get().addStepDefinition(regexp, timeoutMillis, body, ConstantPoolTypeIntrospector.INSTANCE); + JavaBackend.INSTANCE.get().addStepDefinition( + new Java8StepDefinition(regexp, timeoutMillis, StepdefBody.A${arity}.class, body) + ); } <% } %> <% } %> diff --git a/java/src/main/java/cucumber/api/java8/HookBody.java b/java8/src/main/java/cucumber/api/java8/HookBody.java similarity index 100% rename from java/src/main/java/cucumber/api/java8/HookBody.java rename to java8/src/main/java/cucumber/api/java8/HookBody.java diff --git a/java/src/main/java/cucumber/api/java8/HookNoArgsBody.java b/java8/src/main/java/cucumber/api/java8/HookNoArgsBody.java similarity index 100% rename from java/src/main/java/cucumber/api/java8/HookNoArgsBody.java rename to java8/src/main/java/cucumber/api/java8/HookNoArgsBody.java diff --git a/java/src/main/java/cucumber/api/java8/StepdefBody.java b/java8/src/main/java/cucumber/api/java8/StepdefBody.java similarity index 100% rename from java/src/main/java/cucumber/api/java8/StepdefBody.java rename to java8/src/main/java/cucumber/api/java8/StepdefBody.java diff --git a/java8/src/main/java/cucumber/runtime/java8/ConstantPoolTypeIntrospector.java b/java8/src/main/java/cucumber/runtime/java8/ConstantPoolTypeIntrospector.java deleted file mode 100644 index 7f8aafc590..0000000000 --- a/java8/src/main/java/cucumber/runtime/java8/ConstantPoolTypeIntrospector.java +++ /dev/null @@ -1,102 +0,0 @@ -package cucumber.runtime.java8; - -import static java.lang.Class.forName; -import static java.lang.System.arraycopy; -import static jdk.internal.org.objectweb.asm.Type.getObjectType; - -import cucumber.api.java8.StepdefBody; -import cucumber.runtime.CucumberException; -import cucumber.runtime.java.TypeIntrospector; -import sun.reflect.ConstantPool; - -import java.lang.reflect.Method; -import java.lang.reflect.Type; - -public class ConstantPoolTypeIntrospector implements TypeIntrospector { - private static final Method Class_getConstantPool; - private static final int REFERENCE_CLASS = 0; - private static final int REFERENCE_METHOD = 1; - private static final int REFERENCE_ARGUMENT_TYPES = 2; - - static { - try { - Class_getConstantPool = Class.class.getDeclaredMethod("getConstantPool"); - Class_getConstantPool.setAccessible(true); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - public static final TypeIntrospector INSTANCE = new ConstantPoolTypeIntrospector(); - - @Override - public Type[] getGenericTypes(Class clazz, Class interfac3) throws Exception { - final ConstantPool constantPool = (ConstantPool) Class_getConstantPool.invoke(clazz); - final String[] member = getMemberReference(constantPool); - final int parameterCount = interfac3.getTypeParameters().length; - - // Kotlin lambda expression without arguments or closure variables - if (member[REFERENCE_METHOD].equals("INSTANCE")) { - return handleKotlinInstance(); - } - - final jdk.internal.org.objectweb.asm.Type[] argumentTypes = jdk.internal.org.objectweb.asm.Type.getArgumentTypes(member[REFERENCE_ARGUMENT_TYPES]); - - // If we are one parameter short, this is a - // - Reference to an instance method of an arbitrary object of a particular type - if (parameterCount - 1 == argumentTypes.length) { - return handleMethodReferenceToObjectOfType(member[REFERENCE_CLASS], handleLambda(argumentTypes, parameterCount - 1)); - } - // If we are not short on parameters this either - // - Reference to a static method - // - Reference to an instance method of a particular object - // - Reference to a constructor - // - A lambda expression - // We can all treat these as lambda's for figuring out the types. - return handleLambda(argumentTypes, parameterCount); - } - - private static Type[] handleMethodReferenceToObjectOfType(String containingType, Type[] methodArgumentTypes) throws ClassNotFoundException { - Type[] containingTypeAndMethodArgumentTypes = new Type[methodArgumentTypes.length + 1]; - containingTypeAndMethodArgumentTypes[0] = forName(getObjectType(containingType).getClassName()); - arraycopy(methodArgumentTypes, 0, containingTypeAndMethodArgumentTypes, 1, methodArgumentTypes.length); - return containingTypeAndMethodArgumentTypes; - } - - private static Type[] handleLambda(jdk.internal.org.objectweb.asm.Type[] argumentTypes, int typeParameterCount) throws ClassNotFoundException { - if (argumentTypes.length < typeParameterCount) { - throw new CucumberException(String.format("Expected at least %s arguments but found only %s", typeParameterCount, argumentTypes.length)); - } - - // Only look at the N last arguments to the lambda static method, since the first ones might be variables - // who only pass in the states of closed variables - jdk.internal.org.objectweb.asm.Type[] interestingArgumentTypes = new jdk.internal.org.objectweb.asm.Type[typeParameterCount]; - arraycopy(argumentTypes, argumentTypes.length - typeParameterCount, interestingArgumentTypes, 0, typeParameterCount); - - Type[] typeArguments = new Type[typeParameterCount]; - for (int i = 0; i < typeParameterCount; i++) { - typeArguments[i] = forName(interestingArgumentTypes[i].getClassName()); - } - return typeArguments; - } - - private static Type[] handleKotlinInstance() { - return new Type[0]; - } - - private static String[] getMemberReference(ConstantPool constantPool) { - int size = constantPool.getSize(); - - // find last element in constantPool with valid memberRef - // - previously always at size-2 index but changed with JDK 1.8.0_60 - for (int i = size - 1; i > -1; i--) { - try { - return constantPool.getMemberRefInfoAt(i); - } catch (IllegalArgumentException e) { - // eat error; null entry at ConstantPool index? - } - } - throw new CucumberException("Couldn't find memberRef."); - } - -} diff --git a/java/src/main/java/cucumber/runtime/java/Java8HookDefinition.java b/java8/src/main/java/cucumber/runtime/java8/Java8HookDefinition.java similarity index 84% rename from java/src/main/java/cucumber/runtime/java/Java8HookDefinition.java rename to java8/src/main/java/cucumber/runtime/java8/Java8HookDefinition.java index a922e420f1..186af226fe 100644 --- a/java/src/main/java/cucumber/runtime/java/Java8HookDefinition.java +++ b/java8/src/main/java/cucumber/runtime/java8/Java8HookDefinition.java @@ -1,17 +1,17 @@ -package cucumber.runtime.java; +package cucumber.runtime.java8; + +import static java.util.Arrays.asList; import cucumber.api.Scenario; import cucumber.api.java8.HookBody; import cucumber.api.java8.HookNoArgsBody; import cucumber.runtime.HookDefinition; -import cucumber.runtime.Timeout; import cucumber.runtime.TagPredicate; +import cucumber.runtime.Timeout; import gherkin.pickles.PickleTag; import java.util.Collection; -import static java.util.Arrays.asList; - public class Java8HookDefinition implements HookDefinition { private final TagPredicate tagPredicate; private final int order; @@ -44,17 +44,14 @@ public String getLocation(boolean detail) { @Override public void execute(final Scenario scenario) throws Throwable { - Timeout.timeout(new Timeout.Callback() { - @Override - public Object call() throws Throwable { - if (hookBody != null) { - hookBody.accept(scenario); - } else { - hookNoArgsBody.accept(); - } - return null; - + Timeout.timeout(() -> { + if (hookBody != null) { + hookBody.accept(scenario); + } else { + hookNoArgsBody.accept(); } + return null; + }, timeoutMillis); } diff --git a/java/src/main/java/cucumber/runtime/java/Java8StepDefinition.java b/java8/src/main/java/cucumber/runtime/java8/Java8StepDefinition.java similarity index 68% rename from java/src/main/java/cucumber/runtime/java/Java8StepDefinition.java rename to java8/src/main/java/cucumber/runtime/java8/Java8StepDefinition.java index b88a34e90a..0b7fa68f89 100644 --- a/java/src/main/java/cucumber/runtime/java/Java8StepDefinition.java +++ b/java8/src/main/java/cucumber/runtime/java8/Java8StepDefinition.java @@ -1,4 +1,7 @@ -package cucumber.runtime.java; +package cucumber.runtime.java8; + +import static cucumber.runtime.ParameterInfo.fromTypes; +import static net.jodah.typetools.TypeResolver.resolveRawArguments; import cucumber.api.java8.StepdefBody; import cucumber.runtime.Argument; @@ -8,9 +11,9 @@ import cucumber.runtime.StepDefinition; import cucumber.runtime.Utils; import gherkin.pickles.PickleStep; +import net.jodah.typetools.TypeResolver; import java.lang.reflect.Method; -import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; @@ -29,34 +32,22 @@ public class Java8StepDefinition implements StepDefinition { private final List parameterInfos; private final Method method; - public Java8StepDefinition(Pattern pattern, long timeoutMillis, StepdefBody body, TypeIntrospector typeIntrospector) throws Exception { - this.pattern = pattern; + public Java8StepDefinition(String pattern, long timeoutMillis, Class bodyClass, T body) { + this.pattern = Pattern.compile(pattern); this.timeoutMillis = timeoutMillis; this.body = body; - this.argumentMatcher = new JdkPatternArgumentMatcher(pattern); + this.argumentMatcher = new JdkPatternArgumentMatcher(this.pattern); this.location = new Exception().getStackTrace()[3]; - - Class bodyClass = body.getClass(); - - this.method = getAcceptMethod(bodyClass); - this.parameterInfos = getParameterInfos(bodyClass, typeIntrospector, method.getParameterTypes().length); - } - - private List getParameterInfos(Class bodyClass, TypeIntrospector typeIntrospector, int parameterCount) throws Exception { - Type genericInterface = bodyClass.getGenericInterfaces()[0]; - Type[] argumentTypes; - if (genericInterface instanceof ParameterizedType) { - argumentTypes = ((ParameterizedType) genericInterface).getActualTypeArguments(); - } else { - Class interfac3 = (Class) bodyClass.getInterfaces()[0]; - argumentTypes = typeIntrospector.getGenericTypes(bodyClass, interfac3); + this.method = getAcceptMethod(body.getClass()); + try { + Class[] arguments = resolveRawArguments(bodyClass, body.getClass()); + this.parameterInfos = fromTypes(verifyNotListOrMap(arguments)); + } catch (CucumberException e){ + throw e; + } catch (Exception e) { + throw new CucumberException(e); } - Type[] argumentTypesOfCorrectLength = new Type[parameterCount]; - System.arraycopy(argumentTypes, argumentTypes.length - parameterCount, argumentTypesOfCorrectLength, 0, parameterCount); - verifyNotListOrMap(argumentTypesOfCorrectLength); - - return ParameterInfo.fromTypes(argumentTypesOfCorrectLength); } private Method getAcceptMethod(Class bodyClass) { @@ -73,7 +64,7 @@ private Method getAcceptMethod(Class bodyClass) { return acceptMethods.get(0); } - private void verifyNotListOrMap(Type[] argumentTypes) { + private Type[] verifyNotListOrMap(Type[] argumentTypes) { for (Type argumentType : argumentTypes) { if (argumentType instanceof Class) { Class argumentClass = (Class) argumentType; @@ -82,6 +73,7 @@ private void verifyNotListOrMap(Type[] argumentTypes) { } } } + return argumentTypes; } private CucumberException withLocation(CucumberException exception) { diff --git a/java8/src/main/java/cucumber/runtime/java8/LambdaGlueBase.java b/java8/src/main/java/cucumber/runtime/java8/LambdaGlueBase.java index 3991307572..0478a3e7c4 100644 --- a/java8/src/main/java/cucumber/runtime/java8/LambdaGlueBase.java +++ b/java8/src/main/java/cucumber/runtime/java8/LambdaGlueBase.java @@ -13,82 +13,82 @@ public interface LambdaGlueBase extends GlueBase { int DEFAULT_AFTER_ORDER = 1000; default void Before(final HookBody body) { - JavaBackend.INSTANCE.get().addBeforeHookDefinition(EMPTY_TAG_EXPRESSIONS, NO_TIMEOUT, DEFAULT_BEFORE_ORDER, body); + JavaBackend.INSTANCE.get().addBeforeHookDefinition(new Java8HookDefinition(EMPTY_TAG_EXPRESSIONS, DEFAULT_BEFORE_ORDER, NO_TIMEOUT, body)); } default void Before(String[] tagExpressions, final HookBody body) { - JavaBackend.INSTANCE.get().addBeforeHookDefinition(tagExpressions, NO_TIMEOUT, DEFAULT_BEFORE_ORDER, body); + JavaBackend.INSTANCE.get().addBeforeHookDefinition(new Java8HookDefinition(tagExpressions, DEFAULT_BEFORE_ORDER, NO_TIMEOUT, body)); } default void Before(long timeoutMillis, final HookBody body) { - JavaBackend.INSTANCE.get().addBeforeHookDefinition(EMPTY_TAG_EXPRESSIONS, timeoutMillis, DEFAULT_BEFORE_ORDER, body); + JavaBackend.INSTANCE.get().addBeforeHookDefinition(new Java8HookDefinition(EMPTY_TAG_EXPRESSIONS, DEFAULT_BEFORE_ORDER, timeoutMillis, body)); } default void Before(int order, final HookBody body) { - JavaBackend.INSTANCE.get().addBeforeHookDefinition(EMPTY_TAG_EXPRESSIONS, NO_TIMEOUT, order, body); + JavaBackend.INSTANCE.get().addBeforeHookDefinition(new Java8HookDefinition(EMPTY_TAG_EXPRESSIONS, order, NO_TIMEOUT, body)); } default void Before(String[] tagExpressions, long timeoutMillis, int order, final HookBody body) { - JavaBackend.INSTANCE.get().addBeforeHookDefinition(tagExpressions, timeoutMillis, order, body); + JavaBackend.INSTANCE.get().addBeforeHookDefinition(new Java8HookDefinition(tagExpressions, order, timeoutMillis, body)); } default void Before(final HookNoArgsBody body) { - JavaBackend.INSTANCE.get().addBeforeHookDefinition(EMPTY_TAG_EXPRESSIONS, NO_TIMEOUT, DEFAULT_BEFORE_ORDER, body); + JavaBackend.INSTANCE.get().addBeforeHookDefinition(new Java8HookDefinition(EMPTY_TAG_EXPRESSIONS, DEFAULT_BEFORE_ORDER, NO_TIMEOUT, body)); } default void Before(String[] tagExpressions, final HookNoArgsBody body) { - JavaBackend.INSTANCE.get().addBeforeHookDefinition(tagExpressions, NO_TIMEOUT, DEFAULT_BEFORE_ORDER, body); + JavaBackend.INSTANCE.get().addBeforeHookDefinition(new Java8HookDefinition(tagExpressions, DEFAULT_BEFORE_ORDER, NO_TIMEOUT, body)); } default void Before(long timeoutMillis, final HookNoArgsBody body) { - JavaBackend.INSTANCE.get().addBeforeHookDefinition(EMPTY_TAG_EXPRESSIONS, timeoutMillis, DEFAULT_BEFORE_ORDER, body); + JavaBackend.INSTANCE.get().addBeforeHookDefinition(new Java8HookDefinition(EMPTY_TAG_EXPRESSIONS, DEFAULT_BEFORE_ORDER, timeoutMillis, body)); } default void Before(int order, final HookNoArgsBody body) { - JavaBackend.INSTANCE.get().addBeforeHookDefinition(EMPTY_TAG_EXPRESSIONS, NO_TIMEOUT, order, body); + JavaBackend.INSTANCE.get().addBeforeHookDefinition(new Java8HookDefinition(EMPTY_TAG_EXPRESSIONS, order, NO_TIMEOUT, body)); } default void Before(String[] tagExpressions, long timeoutMillis, int order, final HookNoArgsBody body) { - JavaBackend.INSTANCE.get().addBeforeHookDefinition(tagExpressions, timeoutMillis, order, body); + JavaBackend.INSTANCE.get().addBeforeHookDefinition(new Java8HookDefinition(tagExpressions, order, timeoutMillis, body)); } default void After(final HookBody body) { - JavaBackend.INSTANCE.get().addAfterHookDefinition(EMPTY_TAG_EXPRESSIONS, NO_TIMEOUT, DEFAULT_AFTER_ORDER, body); + JavaBackend.INSTANCE.get().addAfterHookDefinition(new Java8HookDefinition(EMPTY_TAG_EXPRESSIONS, DEFAULT_AFTER_ORDER, NO_TIMEOUT, body)); } default void After(String[] tagExpressions, final HookBody body) { - JavaBackend.INSTANCE.get().addAfterHookDefinition(tagExpressions, NO_TIMEOUT, DEFAULT_AFTER_ORDER, body); + JavaBackend.INSTANCE.get().addAfterHookDefinition(new Java8HookDefinition(tagExpressions, DEFAULT_AFTER_ORDER, NO_TIMEOUT, body)); } default void After(long timeoutMillis, final HookBody body) { - JavaBackend.INSTANCE.get().addAfterHookDefinition(EMPTY_TAG_EXPRESSIONS, timeoutMillis, DEFAULT_AFTER_ORDER, body); + JavaBackend.INSTANCE.get().addAfterHookDefinition(new Java8HookDefinition(EMPTY_TAG_EXPRESSIONS, DEFAULT_AFTER_ORDER, timeoutMillis, body)); } default void After(int order, final HookBody body) { - JavaBackend.INSTANCE.get().addAfterHookDefinition(EMPTY_TAG_EXPRESSIONS, NO_TIMEOUT, order, body); + JavaBackend.INSTANCE.get().addAfterHookDefinition(new Java8HookDefinition(EMPTY_TAG_EXPRESSIONS, order, NO_TIMEOUT, body)); } default void After(String[] tagExpressions, long timeoutMillis, int order, final HookBody body) { - JavaBackend.INSTANCE.get().addAfterHookDefinition(tagExpressions, timeoutMillis, order, body); + JavaBackend.INSTANCE.get().addAfterHookDefinition(new Java8HookDefinition(tagExpressions, order, timeoutMillis, body)); } default void After(final HookNoArgsBody body) { - JavaBackend.INSTANCE.get().addAfterHookDefinition(EMPTY_TAG_EXPRESSIONS, NO_TIMEOUT, DEFAULT_AFTER_ORDER, body); + JavaBackend.INSTANCE.get().addAfterHookDefinition(new Java8HookDefinition(EMPTY_TAG_EXPRESSIONS, DEFAULT_AFTER_ORDER, NO_TIMEOUT, body)); } default void After(String[] tagExpressions, final HookNoArgsBody body) { - JavaBackend.INSTANCE.get().addAfterHookDefinition(tagExpressions, NO_TIMEOUT, DEFAULT_AFTER_ORDER, body); + JavaBackend.INSTANCE.get().addAfterHookDefinition(new Java8HookDefinition(tagExpressions, DEFAULT_AFTER_ORDER, NO_TIMEOUT, body)); } default void After(long timeoutMillis, final HookNoArgsBody body) { - JavaBackend.INSTANCE.get().addAfterHookDefinition(EMPTY_TAG_EXPRESSIONS, timeoutMillis, DEFAULT_AFTER_ORDER, body); + JavaBackend.INSTANCE.get().addAfterHookDefinition(new Java8HookDefinition(EMPTY_TAG_EXPRESSIONS, DEFAULT_AFTER_ORDER, timeoutMillis, body)); } default void After(int order, final HookNoArgsBody body) { - JavaBackend.INSTANCE.get().addAfterHookDefinition(EMPTY_TAG_EXPRESSIONS, NO_TIMEOUT, order, body); + JavaBackend.INSTANCE.get().addAfterHookDefinition(new Java8HookDefinition(EMPTY_TAG_EXPRESSIONS, order, NO_TIMEOUT, body)); } default void After(String[] tagExpressions, long timeoutMillis, int order, final HookNoArgsBody body) { - JavaBackend.INSTANCE.get().addAfterHookDefinition(tagExpressions, timeoutMillis, order, body); + JavaBackend.INSTANCE.get().addAfterHookDefinition(new Java8HookDefinition(tagExpressions, order, timeoutMillis, body)); } } diff --git a/java/src/test/java/cucumber/runtime/java/Java8StepDefinitionTest.java b/java8/src/test/java/cucumber/runtime/java8/Java8AnonInnerClassStepDefinitionTest.java similarity index 66% rename from java/src/test/java/cucumber/runtime/java/Java8StepDefinitionTest.java rename to java8/src/test/java/cucumber/runtime/java8/Java8AnonInnerClassStepDefinitionTest.java index a56eecee6a..daf9f08308 100644 --- a/java/src/test/java/cucumber/runtime/java/Java8StepDefinitionTest.java +++ b/java8/src/test/java/cucumber/runtime/java8/Java8AnonInnerClassStepDefinitionTest.java @@ -1,33 +1,33 @@ -package cucumber.runtime.java; +package cucumber.runtime.java8; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; import cucumber.api.java8.StepdefBody; import cucumber.runtime.CucumberException; +import org.junit.Ignore; import org.junit.Test; import java.util.List; -import java.util.regex.Pattern; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; -public class Java8StepDefinitionTest { +public class Java8AnonInnerClassStepDefinitionTest { @Test public void should_calculate_parameters_count_from_body_with_one_param() throws Exception { - Java8StepDefinition java8StepDefinition = new Java8StepDefinition(Pattern.compile("^I have (\\d) some step (.*)$"), 0, oneParamStep(), null); - assertEquals(new Integer(1), java8StepDefinition.getParameterCount()); + Java8StepDefinition java8StepDefinition = new Java8StepDefinition("^I have (\\d) some step (.*)$", 0, StepdefBody.A1.class, oneParamStep()); + assertEquals(Integer.valueOf(1), java8StepDefinition.getParameterCount()); } @Test public void should_calculate_parameters_count_from_body_with_two_params() throws Exception { - Java8StepDefinition java8StepDefinition = new Java8StepDefinition(Pattern.compile("^I have some step $"), 0, twoParamStep(), null); - assertEquals(new Integer(2), java8StepDefinition.getParameterCount()); + Java8StepDefinition java8StepDefinition = new Java8StepDefinition("^I have some step $", 0, StepdefBody.A2.class, twoParamStep()); + assertEquals(Integer.valueOf(2), java8StepDefinition.getParameterCount()); } @Test public void should_fail_for_param_with_non_generic_list() throws Exception { try { - new Java8StepDefinition(Pattern.compile("^I have (\\d) some step (.*)$"), 0, nonGenericListStep(), null); + new Java8StepDefinition("^I have (\\d) some step (.*)$", 0, StepdefBody.A1.class, nonGenericListStep()); fail(); } catch (CucumberException expected) { assertEquals("Can't use java.util.List in lambda step definition. Declare a DataTable argument instead and convert manually with asList/asLists/asMap/asMaps", expected.getMessage()); @@ -35,12 +35,13 @@ public void should_fail_for_param_with_non_generic_list() throws Exception { } @Test + @Ignore public void should_pass_for_param_with_generic_list() throws Exception { - Java8StepDefinition java8StepDefinition = new Java8StepDefinition(Pattern.compile("^I have (\\d) some step (.*)$"), 0, genericListStep(), null); - assertEquals(new Integer(1), java8StepDefinition.getParameterCount()); + Java8StepDefinition java8StepDefinition = new Java8StepDefinition("^I have (\\d) some step (.*)$", 0, StepdefBody.A1.class, genericListStep()); + assertEquals(Integer.valueOf(1), java8StepDefinition.getParameterCount()); } - private StepdefBody oneParamStep() { + private StepdefBody.A1 oneParamStep() { return new StepdefBody.A1() { @Override public void accept(String p1) { @@ -48,7 +49,7 @@ public void accept(String p1) { }; } - private StepdefBody twoParamStep() { + private StepdefBody.A2 twoParamStep() { return new StepdefBody.A2() { @Override public void accept(String p1, String p2) { @@ -56,7 +57,7 @@ public void accept(String p1, String p2) { }; } - private StepdefBody genericListStep() { + private StepdefBody.A1 genericListStep() { return new StepdefBody.A1>() { @Override public void accept(List p1) { @@ -65,7 +66,7 @@ public void accept(List p1) { }; } - private StepdefBody nonGenericListStep() { + private StepdefBody.A1 nonGenericListStep() { return new StepdefBody.A1() { @Override public void accept(List p1) { diff --git a/java8/src/test/java/cucumber/runtime/java8/Java8LambdaStepDefinitionTest.java b/java8/src/test/java/cucumber/runtime/java8/Java8LambdaStepDefinitionTest.java new file mode 100644 index 0000000000..db8d653f99 --- /dev/null +++ b/java8/src/test/java/cucumber/runtime/java8/Java8LambdaStepDefinitionTest.java @@ -0,0 +1,50 @@ +package cucumber.runtime.java8; + +import static org.junit.Assert.assertEquals; + +import cucumber.api.java8.StepdefBody; +import cucumber.runtime.CucumberException; +import org.junit.Test; + +import java.util.List; + +public class Java8LambdaStepDefinitionTest { + + @Test + public void should_calculate_parameters_count_from_body_with_one_param() throws Exception { + StepdefBody.A1 body = p1 -> { + }; + Java8StepDefinition def = new Java8StepDefinition("^I have (\\d) some step (.*)$", 0, StepdefBody.A1.class, body); + assertEquals(Integer.valueOf(1), def.getParameterCount()); + } + + @Test + public void should_calculate_parameters_count_from_body_with_two_params() throws Exception { + StepdefBody.A2 body = (p1, p2) -> { + }; + Java8StepDefinition def = new Java8StepDefinition("^I have some step $", 0, StepdefBody.A2.class, body); + assertEquals(Integer.valueOf(2), def.getParameterCount()); + } + + @Test + public void should_fail_for_param_with_non_generic_list() throws Exception { + try { + StepdefBody.A1 body = p1 -> { + }; + new Java8StepDefinition("^I have (\\d) some step (.*)$", 0, StepdefBody.A1.class, body); + } catch (CucumberException expected) { + assertEquals("Can't use java.util.List in lambda step definition. Declare a DataTable argument instead and convert manually with asList/asLists/asMap/asMaps", expected.getMessage()); + } + } + + @Test + public void should_fail_for_param_with_generic_list() throws Exception { + try { + StepdefBody.A1> body = p1 -> { + }; + new Java8StepDefinition("^I have (\\d) some step (.*)$", 0, StepdefBody.A1.class, body); + } catch (CucumberException expected) { + assertEquals("Can't use java.util.List in lambda step definition. Declare a DataTable argument instead and convert manually with asList/asLists/asMap/asMaps", expected.getMessage()); + } + } +} diff --git a/java8/src/test/java/cucumber/runtime/java8/test/AnonInnerClassStepdefs.java b/java8/src/test/java/cucumber/runtime/java8/test/AnonInnerClassStepdefs.java new file mode 100644 index 0000000000..7025c13dfa --- /dev/null +++ b/java8/src/test/java/cucumber/runtime/java8/test/AnonInnerClassStepdefs.java @@ -0,0 +1,24 @@ +package cucumber.runtime.java8.test; + +import static org.junit.Assert.assertEquals; + +import cucumber.api.java8.GlueBase; +import cucumber.api.java8.StepdefBody; +import cucumber.runtime.java.JavaBackend; +import cucumber.runtime.java8.Java8StepDefinition; + +public class AnonInnerClassStepdefs implements GlueBase { + + public AnonInnerClassStepdefs() { + JavaBackend.INSTANCE.get().addStepDefinition( + new Java8StepDefinition( + "^I have (\\d+) java7 beans in my (.*)", 0, StepdefBody.A2.class, + new StepdefBody.A2() { + @Override + public void accept(Integer cukes, String what) throws Throwable { + assertEquals(42, cukes.intValue()); + assertEquals("belly", what); + } + })); + } +} diff --git a/java8/src/test/java/cucumber/runtime/java8/test/Java8StepDefinitionTest.java b/java8/src/test/java/cucumber/runtime/java8/test/Java8StepDefinitionTest.java deleted file mode 100644 index 7616cd6898..0000000000 --- a/java8/src/test/java/cucumber/runtime/java8/test/Java8StepDefinitionTest.java +++ /dev/null @@ -1,56 +0,0 @@ -package cucumber.runtime.java8.test; - -import cucumber.api.java8.StepdefBody; -import cucumber.runtime.CucumberException; -import cucumber.runtime.java.Java8StepDefinition; -import cucumber.runtime.java.TypeIntrospector; -import cucumber.runtime.java8.ConstantPoolTypeIntrospector; -import org.junit.Test; - -import java.util.List; -import java.util.regex.Pattern; - -import static org.junit.Assert.assertEquals; - -public class Java8StepDefinitionTest { - private final TypeIntrospector typeIntrospector = new ConstantPoolTypeIntrospector(); - - @Test - public void should_calculate_parameters_count_from_body_with_one_param() throws Exception { - StepdefBody body = (StepdefBody.A1) p1 -> { - }; - Java8StepDefinition def = new Java8StepDefinition(Pattern.compile("^I have (\\d) some step (.*)$"), 0, body, typeIntrospector); - assertEquals(new Integer(1), def.getParameterCount()); - } - - @Test - public void should_calculate_parameters_count_from_body_with_two_params() throws Exception { - StepdefBody body = (StepdefBody.A2) (p1, p2) -> { - }; - Java8StepDefinition def = new Java8StepDefinition(Pattern.compile("^I have some step $"), 0, body, typeIntrospector); - assertEquals(new Integer(2), def.getParameterCount()); - } - - @Test - public void should_fail_for_param_with_non_generic_list() throws Exception { - try { - StepdefBody body = (StepdefBody.A1) p1 -> { - }; - new Java8StepDefinition(Pattern.compile("^I have (\\d) some step (.*)$"), 0, body, typeIntrospector); - } catch (CucumberException expected) { - assertEquals("Can't use java.util.List in lambda step definition. Declare a DataTable argument instead and convert manually with asList/asLists/asMap/asMaps", expected.getMessage()); - } - } - - @Test - public void should_pass_for_param_with_generic_list() throws Exception { - try { - StepdefBody body = (StepdefBody.A1>) p1 -> { - }; - new Java8StepDefinition(Pattern.compile("^I have (\\d) some step (.*)$"), 0, body, typeIntrospector); - } catch (CucumberException expected) { - assertEquals("Can't use java.util.List in lambda step definition. Declare a DataTable argument instead and convert manually with asList/asLists/asMap/asMaps", expected.getMessage()); - } - } - -} diff --git a/java8/src/test/java/cucumber/runtime/java8/test/LambdaStepdefs.java b/java8/src/test/java/cucumber/runtime/java8/test/LambdaStepdefs.java index 8596f182e5..e6cb917ec8 100644 --- a/java8/src/test/java/cucumber/runtime/java8/test/LambdaStepdefs.java +++ b/java8/src/test/java/cucumber/runtime/java8/test/LambdaStepdefs.java @@ -5,6 +5,7 @@ import static org.junit.Assert.assertTrue; import cucumber.api.DataTable; +import cucumber.api.PendingException; import cucumber.api.Scenario; import cucumber.api.java8.En; @@ -65,12 +66,14 @@ public LambdaStepdefs() { Given("^A method reference that declares an exception$", this::methodThatDeclaresException); Given("^A method reference with an argument (\\d+)$", this::methodWithAnArgument); + Given("^A method reference with an int argument (\\d+)$", this::methodWithAnIntArgument); + Given("^A constructor reference with an argument (.*)$", Contact::new); Given("^A static method reference with an argument (\\d+)$", LambdaStepdefs::staticMethodWithAnArgument); - Given("^A method reference to an arbitrary object of a particular type (\\d+)$", Contact::call); Given("^A method reference to an arbitrary object of a particular type (.*) with argument (.*)$", Contact::update); + } private void methodThatDeclaresException() throws Throwable { @@ -78,6 +81,9 @@ private void methodThatDeclaresException() throws Throwable { private void methodWithAnArgument(Integer cuckes) throws Throwable { assertEquals(42, cuckes.intValue()); } + private void methodWithAnIntArgument(int cuckes) throws Throwable { + assertEquals(42, cuckes); + } public static void staticMethodWithAnArgument(Integer cuckes) throws Throwable { assertEquals(42, cuckes.intValue()); diff --git a/java/src/test/resources/cucumber/runtime/java/java8test/java8.feature b/java8/src/test/resources/cucumber/runtime/java8/test/anon-inner-class-step-definitions.feature similarity index 56% rename from java/src/test/resources/cucumber/runtime/java/java8test/java8.feature rename to java8/src/test/resources/cucumber/runtime/java8/test/anon-inner-class-step-definitions.feature index ab0f99c7d4..a5397dd88c 100644 --- a/java/src/test/resources/cucumber/runtime/java/java8test/java8.feature +++ b/java8/src/test/resources/cucumber/runtime/java8/test/anon-inner-class-step-definitions.feature @@ -1,3 +1,3 @@ Feature: Java8 Scenario: use the API with Java7 style - Given I have 42 cukes in my belly \ No newline at end of file + Given I have 42 java7 beans in my belly \ No newline at end of file diff --git a/java8/src/test/resources/cucumber/runtime/java8/test/java8.feature b/java8/src/test/resources/cucumber/runtime/java8/test/lambda-step-definitions.feature similarity index 95% rename from java8/src/test/resources/cucumber/runtime/java8/test/java8.feature rename to java8/src/test/resources/cucumber/runtime/java8/test/lambda-step-definitions.feature index ba51718aee..3c20a7674a 100644 --- a/java8/src/test/resources/cucumber/runtime/java8/test/java8.feature +++ b/java8/src/test/resources/cucumber/runtime/java8/test/lambda-step-definitions.feature @@ -24,6 +24,7 @@ Feature: Java8 Scenario: using method references Given A method reference that declares an exception Given A method reference with an argument 42 + Given A method reference with an int argument 42 Given A static method reference with an argument 42 Given A constructor reference with an argument 42 Given A method reference to an arbitrary object of a particular type 42 diff --git a/pom.xml b/pom.xml index 3e9e97d271..00d1dbe89c 100644 --- a/pom.xml +++ b/pom.xml @@ -77,6 +77,7 @@ 2.2.0 5.4.0 1.0.1 + 0.5.0 @@ -542,6 +543,11 @@ org.apache.felix.framework ${felix.version} + + net.jodah + typetools + ${typetools.version} +