Skip to content

Commit

Permalink
[Kotlin Java8] Support closures
Browse files Browse the repository at this point in the history
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
  • Loading branch information
mpkorstanje committed Jun 14, 2017
1 parent 004e192 commit 168bdb3
Show file tree
Hide file tree
Showing 8 changed files with 215 additions and 4 deletions.
1 change: 1 addition & 0 deletions History.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## [2.0.0-SNAPSHOT](https://github.com/cucumber/cucumber-jvm/compare/v1.2.5...master) (In Git)

* [Java8, Kotlin Java8] Support java 8 method references ([#1140](https://github.com/cucumber/cucumber-jvm/pull/1140) M.P. Korstanje)
* [Core] Show explicit error message when field name missed in table header ([#1014](https://github.com/cucumber/cucumber-jvm/pull/1014) Mykola Gurov)
* [Examples] Properly quit selenium in webbit examples ([#1146](https://github.com/cucumber/cucumber-jvm/pull/1146) Alberto Scotto)
* [JUnit] Use AssumptionFailed to mark scenarios/steps as skipped ([#1142](https://github.com/cucumber/cucumber-jvm/pull/1142) Björn Rasmusson)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ public Type[] getGenericTypes(Class<? extends StepdefBody> clazz, Class<? extend
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
Expand Down Expand Up @@ -75,6 +80,10 @@ private static Type[] handleLambda(jdk.internal.org.objectweb.asm.Type[] argumen
return typeArguments;
}

private static Type[] handleKotlinInstance() {
return new Type[0];
}

private static String[] getMemberReference(ConstantPool constantPool) {
int size = constantPool.getSize();

Expand Down
6 changes: 6 additions & 0 deletions kotlin-java8/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Java 8 Bindings for Kotlin
==========================

This module only runs tests.

You can use `cucumber-java` or `cucumber-java8` directly in Kotlin.
93 changes: 93 additions & 0 deletions kotlin-java8/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<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">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-jvm</artifactId>
<relativePath>../pom.xml</relativePath>
<version>2.0.0-SNAPSHOT</version>
</parent>

<artifactId>cucumber-kotlin-java8</artifactId>
<packaging>jar</packaging>
<name>Cucumber-JVM: Kotlin Java8</name>

<properties>
<kotlin.version>1.1.2-2</kotlin.version>
</properties>

<dependencies>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-java8</artifactId>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.sourceforge.cobertura</groupId>
<artifactId>cobertura</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>${kotlin.version}</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
<testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
<plugins>
<plugin>
<artifactId>kotlin-maven-plugin</artifactId>
<groupId>org.jetbrains.kotlin</groupId>
<version>${kotlin.version}</version>
<executions>
<execution>
<id>compile</id>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile</id>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>

</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package cucumber.runtime.kotlin.test;

import cucumber.api.DataTable
import cucumber.api.Scenario
import cucumber.api.java8.En
import org.junit.Assert.*

var lastInstance : LambdaStepdefs? = null

class LambdaStepdefs : En {

init {
Before { scenario: Scenario ->
assertNotSame(this, lastInstance)
lastInstance = this
}

Given("^this data table:$") { peopleTable: DataTable ->
val people = peopleTable.asList(Person::class.java)
assertEquals("Aslak", people[0].first)
assertEquals("Hellesøy", people[0].last)
}

val alreadyHadThisManyCukes = 1
Given("^I have (\\d+) cukes in my belly$") { n: Long ->
assertEquals(1, alreadyHadThisManyCukes)
assertEquals(42L, n)
}

val localState = "hello"
Then("^I really have (\\d+) cukes in my belly") { i: Int ->
assertEquals(42, i)
assertEquals("hello", localState)
}

Given("^A statement with a body expression$") { assertTrue(true) }

Given("^A statement with a simple match$", { -> assertTrue(true) })

val localInt = 1
Given("^A statement with a scoped argument$", { assertEquals(2, localInt + 1) })

Given("^I will give you (\\d+) and ([\\d\\.]+) and (\\w+) and (\\d+)$") { a: Int, b: Float, c: String, d: Int ->
assertEquals(1, a)
assertEquals(2.2f, b)
assertEquals("three", c)
assertEquals(4, d)
}
}

class Person {
internal var first: String? = null
internal var last: String? = null
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package cucumber.runtime.kotlin.test

import cucumber.api.junit.Cucumber
import org.junit.runner.RunWith

@RunWith(Cucumber::class)
class RunCukesTest {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
Feature: Kotlin

Scenario: use the API with Java8 style
Given I have 42 cukes in my belly
Then I really have 42 cukes in my belly

Scenario: another scenario which should have isolated state
Given I have 42 cukes in my belly
And something that isn't defined

Scenario: Parameterless lambdas
Given A statement with a simple match
Given A statement with a scoped argument

Scenario: I can use body expressions
Given A statement with a body expression

Scenario: Multi-param lambdas
Given I will give you 1 and 2.2 and three and 4

Scenario: use a table
Given this data table:
| first | last |
| Aslak | Hellesøy |
| Donald | Duck |
22 changes: 18 additions & 4 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<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">
<modelVersion>4.0.0</modelVersion>
<modelVersion>4.0.0</modelVersion>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-jvm</artifactId>
<version>2.0.0-SNAPSHOT</version>
Expand Down Expand Up @@ -192,6 +192,16 @@
<artifactId>android-examples</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-java8</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-kotlin-java8</artifactId>
<version>${project.version}</version>
</dependency>

<!-- Spring stuff -->
<dependency>
Expand Down Expand Up @@ -581,6 +591,7 @@
</activation>
<modules>
<module>java8</module>
<module>kotlin-java8</module>
</modules>
</profile>

Expand Down Expand Up @@ -754,7 +765,8 @@
<configuration>
<aggregate>true</aggregate>
<use>false</use>
<excludePackageNames>cucumber.runtime,cucumber.runtime.*</excludePackageNames>
<excludePackageNames>cucumber.runtime,cucumber.runtime.*
</excludePackageNames>
<links>
<link>http://docs.oracle.com/javase/7/docs/api/</link>
<link>http://junit.sourceforge.net/javadoc/</link>
Expand Down Expand Up @@ -922,12 +934,14 @@
<version>[${minimum.maven.version},)</version>
</requireMavenVersion>
<requirePluginVersions>
<message>Best Practice is to always define plugin versions!</message>
<message>Best Practice is to always define plugin versions!
</message>
<banLatest>true</banLatest>
<banRelease>true</banRelease>
<banSnapshots>true</banSnapshots>
<phases>clean,deploy,site</phases>
<unCheckedPluginList>org.codehaus.mojo:cobertura-maven-plugin</unCheckedPluginList>
<unCheckedPluginList>org.codehaus.mojo:cobertura-maven-plugin
</unCheckedPluginList>
</requirePluginVersions>
<bannedDependencies>
<excludes>
Expand Down

0 comments on commit 168bdb3

Please sign in to comment.