Skip to content

Commit f2354e8

Browse files
Anton Deriabinmpkorstanje
Anton Deriabin
authored andcommitted
[Java8] Add lambda parameter type annotations (#1768)
Implements a Java8 equivalent for #1677.
1 parent f68be2c commit f2354e8

20 files changed

+732
-11
lines changed

core/src/main/java/io/cucumber/core/runner/PickleStepDefinitionMatch.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import io.cucumber.cucumberexpressions.CucumberExpressionException;
1111
import io.cucumber.datatable.CucumberDataTableException;
1212
import io.cucumber.datatable.UndefinedDataTableTypeException;
13+
import io.cucumber.docstring.CucumberDocStringException;
1314

1415
import java.net.URI;
1516
import java.util.ArrayList;
@@ -45,7 +46,7 @@ public void runStep(TestCaseState state) throws Throwable {
4546
}
4647
} catch (UndefinedDataTableTypeException e) {
4748
throw registerTypeInConfiguration(e);
48-
} catch (CucumberExpressionException | CucumberDataTableException e) {
49+
} catch (CucumberExpressionException | CucumberDataTableException | CucumberDocStringException e) {
4950
throw couldNotConvertArguments(e);
5051
} catch (CucumberBackendException e) {
5152
throw couldNotInvokeArgumentConversion(e);

core/src/test/java/io/cucumber/core/runner/StepDefinitionMatchTest.java

+31
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import io.cucumber.core.stepexpression.StepTypeRegistry;
1212
import io.cucumber.cucumberexpressions.ParameterType;
1313
import io.cucumber.datatable.DataTableType;
14+
import io.cucumber.docstring.DocStringType;
1415
import org.junit.jupiter.api.Test;
1516
import org.junit.jupiter.api.function.Executable;
1617

@@ -241,6 +242,36 @@ void throws_could_not_convert_exception_for_singleton_table_dimension_mismatch()
241242
)));
242243
}
243244

245+
@Test
246+
void throws_could_not_convert_exception_for_docstring() {
247+
CucumberFeature feature = TestFeatureParser.parse("" +
248+
"Feature: Test feature\n" +
249+
" Scenario: Test scenario\n" +
250+
" Given I have some cukes in my belly\n" +
251+
" \"\"\"doc\n" +
252+
" converting this should throw an exception\n" +
253+
" \"\"\"\n"
254+
);
255+
256+
stepTypeRegistry.defineDocStringType(new DocStringType(ItemQuantity.class, "doc", content -> {
257+
throw new IllegalArgumentException(content);
258+
}));
259+
260+
CucumberStep step = feature.getPickles().get(0).getSteps().get(0);
261+
StepDefinition stepDefinition = new StubStepDefinition("I have some cukes in my belly", ItemQuantity.class);
262+
CoreStepDefinition coreStepDefinition = new CoreStepDefinition(stepDefinition, stepTypeRegistry);
263+
List<Argument> arguments = coreStepDefinition.matchedArguments(step);
264+
265+
StepDefinitionMatch stepDefinitionMatch = new PickleStepDefinitionMatch(arguments, stepDefinition, null, step);
266+
267+
Executable testMethod = () -> stepDefinitionMatch.runStep(null);
268+
CucumberException actualThrown = assertThrows(CucumberException.class, testMethod);
269+
assertThat(actualThrown.getMessage(), is(equalTo(
270+
"Could not convert arguments for step [I have some cukes in my belly] defined at '{stubbed location with details}'.\n" +
271+
"The details are in the stacktrace below."
272+
)));
273+
}
274+
244275
@Test
245276
void throws_could_not_invoke_argument_conversion_when_argument_could_not_be_got() {
246277
CucumberFeature feature = TestFeatureParser.parse("file:test.feature", "" +

docstring/pom.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
<artifactId>docstring</artifactId>
1313
<packaging>jar</packaging>
14-
<name>Docstring</name>
14+
<name>Cucumber-JVM: Docstring</name>
1515

1616
<properties>
1717
<project.Automatic-Module-Name>io.cucumber.docstring</project.Automatic-Module-Name>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package io.cucumber.java8;
2+
3+
import org.apiguardian.api.API;
4+
5+
@FunctionalInterface
6+
@API(status = API.Status.STABLE)
7+
public interface DataTableCellDefinitionBody<T> {
8+
T accept(String cell) throws Throwable;
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package io.cucumber.java8;
2+
3+
import io.cucumber.datatable.DataTable;
4+
import org.apiguardian.api.API;
5+
6+
@FunctionalInterface
7+
@API(status = API.Status.STABLE)
8+
public interface DataTableDefinitionBody<T> {
9+
T accept(DataTable dataTable) throws Throwable;
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package io.cucumber.java8;
2+
3+
import java.util.Map;
4+
import org.apiguardian.api.API;
5+
6+
@FunctionalInterface
7+
@API(status = API.Status.STABLE)
8+
public interface DataTableEntryDefinitionBody<T> {
9+
T accept(Map<String, String> map) throws Throwable;
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package io.cucumber.java8;
2+
3+
import java.util.List;
4+
import org.apiguardian.api.API;
5+
6+
@FunctionalInterface
7+
@API(status = API.Status.STABLE)
8+
public interface DataTableRowDefinitionBody<T> {
9+
T accept(List<String> row) throws Throwable;
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package io.cucumber.java8;
2+
3+
import org.apiguardian.api.API;
4+
5+
@FunctionalInterface
6+
@API(status = API.Status.STABLE)
7+
public interface DocStringDefinitionBody<T> {
8+
T accept(String docString) throws Throwable;
9+
}

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

+18
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@
22

33
import io.cucumber.core.backend.Backend;
44
import io.cucumber.core.backend.Container;
5+
import io.cucumber.core.backend.DataTableTypeDefinition;
6+
import io.cucumber.core.backend.DocStringTypeDefinition;
57
import io.cucumber.core.backend.Glue;
68
import io.cucumber.core.backend.HookDefinition;
79
import io.cucumber.core.backend.Lookup;
10+
import io.cucumber.core.backend.ParameterTypeDefinition;
811
import io.cucumber.core.backend.Snippet;
912
import io.cucumber.core.backend.StepDefinition;
1013
import io.cucumber.core.io.ClassFinder;
@@ -105,7 +108,22 @@ public void addBeforeHookDefinition(HookDefinition beforeHook) {
105108
@Override
106109
public void addAfterHookDefinition(HookDefinition afterHook) {
107110
glue.addAfterHook(afterHook);
111+
}
112+
113+
@Override
114+
public void addDocStringType(DocStringTypeDefinition docStringTypeDefinition) {
115+
glue.addDocStringType(docStringTypeDefinition);
116+
117+
}
108118

119+
@Override
120+
public void addDataTableType(DataTableTypeDefinition dataTableTypeDefinition) {
121+
glue.addDataTableType(dataTableTypeDefinition);
122+
}
123+
124+
@Override
125+
public void addParameterType(ParameterTypeDefinition parameterTypeDefinition) {
126+
glue.addParameterType(parameterTypeDefinition);
109127
}
110128
}
111129
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package io.cucumber.java8;
2+
3+
import io.cucumber.core.backend.DataTableTypeDefinition;
4+
import io.cucumber.datatable.DataTable;
5+
import io.cucumber.datatable.DataTableType;
6+
import io.cucumber.datatable.TableCellTransformer;
7+
import io.cucumber.datatable.TableEntryTransformer;
8+
import io.cucumber.datatable.TableRowTransformer;
9+
import io.cucumber.datatable.TableTransformer;
10+
11+
import java.lang.reflect.Method;
12+
import java.lang.reflect.Type;
13+
import java.util.List;
14+
import java.util.Map;
15+
import net.jodah.typetools.TypeResolver;
16+
17+
final class Java8DataTableTypeDefinition extends AbstractGlueDefinition implements DataTableTypeDefinition {
18+
19+
private final DataTableType dataTableType;
20+
21+
Java8DataTableTypeDefinition(Object body) {
22+
super(body, new Exception().getStackTrace()[3]);
23+
this.dataTableType = createDataTableType(method);
24+
}
25+
26+
private DataTableType createDataTableType(Method method) {
27+
Class returnType = TypeResolver.resolveRawArguments(DataTableEntryDefinitionBody.class, body.getClass())[0];
28+
Type[] parameterTypes = method.getGenericParameterTypes();
29+
Type parameterType = parameterTypes[0];
30+
31+
if (DataTable.class.equals(parameterType)) {
32+
return new DataTableType( returnType, (TableTransformer<Object>) this::execute);
33+
}
34+
35+
if (List.class.equals(parameterType)) {
36+
return new DataTableType(returnType, (TableRowTransformer<Object>) this::execute);
37+
}
38+
39+
if (Map.class.equals(parameterType)) {
40+
return new DataTableType(returnType, (TableEntryTransformer<Object>) this::execute);
41+
}
42+
43+
if (String.class.equals(parameterType)) {
44+
return new DataTableType(returnType, (TableCellTransformer<Object>) this::execute);
45+
}
46+
47+
return new DataTableType(returnType, (TableTransformer<Object>) this::execute);
48+
}
49+
50+
@Override
51+
public DataTableType dataTableType() {
52+
return dataTableType;
53+
}
54+
55+
private Object execute(Object arg) throws Throwable {
56+
return Invoker.invoke(this, body, method, arg);
57+
}
58+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package io.cucumber.java8;
2+
3+
import io.cucumber.core.backend.DocStringTypeDefinition;
4+
import io.cucumber.core.exception.CucumberException;
5+
import io.cucumber.docstring.DocStringType;
6+
import net.jodah.typetools.TypeResolver;
7+
8+
final class Java8DocStringTypeDefinition extends AbstractGlueDefinition implements DocStringTypeDefinition {
9+
10+
private final DocStringType docStringType;
11+
12+
@Override
13+
public DocStringType docStringType() {
14+
return docStringType;
15+
}
16+
17+
Java8DocStringTypeDefinition(Object body, String contentType) {
18+
super(body, new Exception().getStackTrace()[3]);
19+
if (contentType == null) {
20+
throw new CucumberException("Docstring content type couldn't be null, define docstring content type");
21+
}
22+
if (contentType.isEmpty()) {
23+
throw new CucumberException("Docstring content type couldn't be empty, define docstring content type");
24+
}
25+
Class returnType = TypeResolver.resolveRawArguments(DocStringDefinitionBody.class, body.getClass())[0];
26+
this.docStringType = new DocStringType(
27+
returnType,
28+
contentType,
29+
this::execute);
30+
}
31+
32+
private Object execute(String content) throws Throwable {
33+
return Invoker.invoke(this, body, method, content);
34+
}
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package io.cucumber.java8;
2+
3+
import java.util.Collections;
4+
5+
import io.cucumber.core.backend.ParameterTypeDefinition;
6+
import io.cucumber.cucumberexpressions.ParameterType;
7+
8+
import static net.jodah.typetools.TypeResolver.resolveRawArguments;
9+
10+
class Java8ParameterTypeDefinition extends AbstractGlueDefinition implements ParameterTypeDefinition {
11+
12+
private final ParameterType parameterType;
13+
14+
@Override
15+
public ParameterType<?> parameterType() {
16+
return parameterType;
17+
}
18+
19+
<T extends ParameterDefinitionBody> Java8ParameterTypeDefinition(String name, String regex, Class<T> bodyClass, T body) {
20+
super(body, new Exception().getStackTrace()[3]);
21+
Class<?> returnType = resolveRawArguments(bodyClass, body.getClass())[0];
22+
this.parameterType = new ParameterType(name, Collections.singletonList(regex), returnType, this::execute);
23+
}
24+
25+
private Object execute(String[] parameterContent) throws Throwable {
26+
return Invoker.invoke(this, body, method, parameterContent);
27+
}
28+
}

java8/src/main/java/io/cucumber/java8/Java8StepDefinition.java

+1-9
Original file line numberDiff line numberDiff line change
@@ -13,27 +13,19 @@
1313

1414
final class Java8StepDefinition extends AbstractGlueDefinition implements StepDefinition {
1515

16-
private final long timeoutMillis;
1716
private final List<ParameterInfo> parameterInfos;
1817
private final String expression;
1918
private <T extends StepDefinitionBody> Java8StepDefinition(String expression,
20-
long timeoutMillis,
2119
Class<T> bodyClass,
2220
T body) {
2321
super(body, new Exception().getStackTrace()[3]);
24-
this.timeoutMillis = timeoutMillis;
2522
this.expression = requireNonNull(expression, "cucumber-expression may not be null");
2623
this.parameterInfos = fromTypes(expression, location, resolveRawArguments(bodyClass, body.getClass()));
2724
}
2825

2926
public static <T extends StepDefinitionBody> Java8StepDefinition create(
3027
String expression, Class<T> bodyClass, T body) {
31-
return new Java8StepDefinition(expression, 0, bodyClass, body);
32-
}
33-
34-
public static <T extends StepDefinitionBody> StepDefinition create(
35-
String expression, long timeoutMillis, Class<T> bodyClass, T body) {
36-
return new Java8StepDefinition(expression, timeoutMillis, bodyClass, body);
28+
return new Java8StepDefinition(expression, bodyClass, body);
3729
}
3830

3931
private static List<ParameterInfo> fromTypes(String expression, StackTraceElement location, Type[] genericParameterTypes) {

0 commit comments

Comments
 (0)