Skip to content

Commit 762cafb

Browse files
committed
[Kotlin Java8] Support closures
The implementation of t he `ConstantPoolTypeIntrospector` did not fully support closures. Using a closure without scoped variables would result in a null pointer exception. E.g: ```java Given("^A statement with a body expression$") { assertTrue(true) } ``` This is resolved check if the member reference method equals the magic constant "INSTANCE" and return no type arguments. Related issues: - #1123 - #1126 This fixes #1123
1 parent 004e192 commit 762cafb

File tree

8 files changed

+209
-1
lines changed

8 files changed

+209
-1
lines changed

History.md

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

3+
* [Java8, Kotlin Java8] Support java 8 method references ([#1140](https://github.com/cucumber/cucumber-jvm/pull/1140) M.P. Korstanje)
34
* [Core] Show explicit error message when field name missed in table header ([#1014](https://github.com/cucumber/cucumber-jvm/pull/1014) Mykola Gurov)
45
* [Examples] Properly quit selenium in webbit examples ([#1146](https://github.com/cucumber/cucumber-jvm/pull/1146) Alberto Scotto)
56
* [JUnit] Use AssumptionFailed to mark scenarios/steps as skipped ([#1142](https://github.com/cucumber/cucumber-jvm/pull/1142) Björn Rasmusson)

java8/src/main/java/cucumber/runtime/java8/ConstantPoolTypeIntrospector.java

+9
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ public Type[] getGenericTypes(Class<? extends StepdefBody> clazz, Class<? extend
3535
final String[] member = getMemberReference(constantPool);
3636
final int parameterCount = interfac3.getTypeParameters().length;
3737

38+
// Kotlin lambda expression without arguments or closure variables
39+
if (member[REFERENCE_METHOD].equals("INSTANCE")) {
40+
return handleKotlinInstance();
41+
}
42+
3843
final jdk.internal.org.objectweb.asm.Type[] argumentTypes = jdk.internal.org.objectweb.asm.Type.getArgumentTypes(member[REFERENCE_ARGUMENT_TYPES]);
3944

4045
// If we are one parameter short, this is a
@@ -75,6 +80,10 @@ private static Type[] handleLambda(jdk.internal.org.objectweb.asm.Type[] argumen
7580
return typeArguments;
7681
}
7782

83+
private static Type[] handleKotlinInstance() {
84+
return new Type[0];
85+
}
86+
7887
private static String[] getMemberReference(ConstantPool constantPool) {
7988
int size = constantPool.getSize();
8089

kotlin-java8/README.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Java 8 Bindings for Kotlin
2+
==========================
3+
4+
This module only runs tests.
5+
6+
You can use `cucumber-java` or `cucumber-java8` directly in Kotlin.

kotlin-java8/pom.xml

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0"
2+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
6+
<parent>
7+
<groupId>io.cucumber</groupId>
8+
<artifactId>cucumber-jvm</artifactId>
9+
<relativePath>../pom.xml</relativePath>
10+
<version>2.0.0-SNAPSHOT</version>
11+
</parent>
12+
13+
<artifactId>cucumber-kotlin-java8</artifactId>
14+
<packaging>jar</packaging>
15+
<name>Cucumber-JVM: Kotlin Java8</name>
16+
17+
<properties>
18+
<kotlin.version>1.1.2-2</kotlin.version>
19+
</properties>
20+
21+
<dependencies>
22+
<dependency>
23+
<groupId>io.cucumber</groupId>
24+
<artifactId>cucumber-java8</artifactId>
25+
</dependency>
26+
<dependency>
27+
<groupId>io.cucumber</groupId>
28+
<artifactId>cucumber-junit</artifactId>
29+
<scope>test</scope>
30+
</dependency>
31+
<dependency>
32+
<groupId>junit</groupId>
33+
<artifactId>junit</artifactId>
34+
<scope>test</scope>
35+
</dependency>
36+
<dependency>
37+
<groupId>net.sourceforge.cobertura</groupId>
38+
<artifactId>cobertura</artifactId>
39+
<scope>test</scope>
40+
</dependency>
41+
<dependency>
42+
<groupId>org.jetbrains.kotlin</groupId>
43+
<artifactId>kotlin-stdlib</artifactId>
44+
<version>${kotlin.version}</version>
45+
<scope>test</scope>
46+
</dependency>
47+
</dependencies>
48+
49+
<build>
50+
<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
51+
<testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
52+
<plugins>
53+
<plugin>
54+
<artifactId>kotlin-maven-plugin</artifactId>
55+
<groupId>org.jetbrains.kotlin</groupId>
56+
<version>${kotlin.version}</version>
57+
<executions>
58+
<execution>
59+
<id>compile</id>
60+
<goals>
61+
<goal>compile</goal>
62+
</goals>
63+
</execution>
64+
<execution>
65+
<id>test-compile</id>
66+
<goals>
67+
<goal>test-compile</goal>
68+
</goals>
69+
</execution>
70+
</executions>
71+
</plugin>
72+
<plugin>
73+
<artifactId>maven-jar-plugin</artifactId>
74+
<configuration>
75+
<skip>true</skip>
76+
</configuration>
77+
</plugin>
78+
<plugin>
79+
<artifactId>maven-install-plugin</artifactId>
80+
<configuration>
81+
<skip>true</skip>
82+
</configuration>
83+
</plugin>
84+
<plugin>
85+
<artifactId>maven-deploy-plugin</artifactId>
86+
<configuration>
87+
<skip>true</skip>
88+
</configuration>
89+
</plugin>
90+
</plugins>
91+
</build>
92+
93+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package cucumber.runtime.kotlin.test;
2+
3+
import cucumber.api.DataTable
4+
import cucumber.api.Scenario
5+
import cucumber.api.java8.En
6+
import org.junit.Assert.*
7+
8+
var lastInstance : LambdaStepdefs? = null
9+
10+
class LambdaStepdefs : En {
11+
12+
init {
13+
Before { scenario: Scenario ->
14+
assertNotSame(this, lastInstance)
15+
lastInstance = this
16+
}
17+
18+
Given("^this data table:$") { peopleTable: DataTable ->
19+
val people = peopleTable.asList(Person::class.java)
20+
assertEquals("Aslak", people[0].first)
21+
assertEquals("Hellesøy", people[0].last)
22+
}
23+
24+
val alreadyHadThisManyCukes = 1
25+
Given("^I have (\\d+) cukes in my belly$") { n: Long ->
26+
assertEquals(1, alreadyHadThisManyCukes)
27+
assertEquals(42L, n)
28+
}
29+
30+
val localState = "hello"
31+
Then("^I really have (\\d+) cukes in my belly") { i: Int ->
32+
assertEquals(42, i)
33+
assertEquals("hello", localState)
34+
}
35+
36+
Given("^A statement with a body expression$") { assertTrue(true) }
37+
38+
Given("^A statement with a simple match$", { -> assertTrue(true) })
39+
40+
val localInt = 1
41+
Given("^A statement with a scoped argument$", { assertEquals(2, localInt + 1) })
42+
43+
Given("^I will give you (\\d+) and ([\\d\\.]+) and (\\w+) and (\\d+)$") { a: Int, b: Float, c: String, d: Int ->
44+
assertEquals(1, a)
45+
assertEquals(2.2f, b)
46+
assertEquals("three", c)
47+
assertEquals(4, d)
48+
}
49+
}
50+
51+
class Person {
52+
internal var first: String? = null
53+
internal var last: String? = null
54+
}
55+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package cucumber.runtime.kotlin.test
2+
3+
import cucumber.api.junit.Cucumber
4+
import org.junit.runner.RunWith
5+
6+
@RunWith(Cucumber::class)
7+
class RunCukesTest {
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
Feature: Kotlin
2+
3+
Scenario: use the API with Java8 style
4+
Given I have 42 cukes in my belly
5+
Then I really have 42 cukes in my belly
6+
7+
Scenario: another scenario which should have isolated state
8+
Given I have 42 cukes in my belly
9+
And something that isn't defined
10+
11+
Scenario: Parameterless lambdas
12+
Given A statement with a simple match
13+
Given A statement with a scoped argument
14+
15+
Scenario: I can use body expressions
16+
Given A statement with a body expression
17+
18+
Scenario: Multi-param lambdas
19+
Given I will give you 1 and 2.2 and three and 4
20+
21+
Scenario: use a table
22+
Given this data table:
23+
| first | last |
24+
| Aslak | Hellesøy |
25+
| Donald | Duck |

pom.xml

+12-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
2-
<modelVersion>4.0.0</modelVersion>
2+
<modelVersion>4.0.0</modelVersion>
33
<groupId>io.cucumber</groupId>
44
<artifactId>cucumber-jvm</artifactId>
55
<version>2.0.0-SNAPSHOT</version>
@@ -192,6 +192,16 @@
192192
<artifactId>android-examples</artifactId>
193193
<version>${project.version}</version>
194194
</dependency>
195+
<dependency>
196+
<groupId>io.cucumber</groupId>
197+
<artifactId>cucumber-java8</artifactId>
198+
<version>${project.version}</version>
199+
</dependency>
200+
<dependency>
201+
<groupId>io.cucumber</groupId>
202+
<artifactId>cucumber-kotlin-java8</artifactId>
203+
<version>${project.version}</version>
204+
</dependency>
195205

196206
<!-- Spring stuff -->
197207
<dependency>
@@ -581,6 +591,7 @@
581591
</activation>
582592
<modules>
583593
<module>java8</module>
594+
<module>kotlin-java8</module>
584595
</modules>
585596
</profile>
586597

0 commit comments

Comments
 (0)