Skip to content

Commit 9c96d83

Browse files
committed
Implement scenario scoped glue code for classes
1 parent 72f3be9 commit 9c96d83

19 files changed

+950
-283
lines changed
Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,32 @@
11
package io.cucumber.scala
22

3-
import io.cucumber.core.backend.Located
3+
import java.lang.reflect.InvocationTargetException
44

5-
abstract class AbstractGlueDefinition(location: StackTraceElement) extends Located {
5+
import io.cucumber.core.backend.{CucumberInvocationTargetException, Located}
66

7-
def getLocation(): String = {
7+
import scala.util.{Failure, Try}
8+
9+
trait AbstractGlueDefinition extends Located {
10+
11+
val location: StackTraceElement
12+
13+
override def getLocation(): String = {
814
location.toString
915
}
1016

11-
def isDefinedAt(stackTraceElement: StackTraceElement): Boolean = {
17+
override def isDefinedAt(stackTraceElement: StackTraceElement): Boolean = {
1218
location.getFileName != null && location.getFileName == stackTraceElement.getFileName
1319
}
1420

21+
/**
22+
* Executes the block of code and handle failures in the way asked by Cucumber specification: that is throwing a CucumberInvocationTargetException.
23+
*/
24+
protected def executeAsCucumber(block: => Unit): Unit = {
25+
Try(block)
26+
.recoverWith {
27+
case ex => Failure(new CucumberInvocationTargetException(this, new InvocationTargetException(ex)))
28+
}
29+
.get
30+
}
31+
1532
}

scala/sources/src/main/scala/io/cucumber/scala/GlueAdaptor.scala

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,18 @@ import io.cucumber.core.backend.Glue
44

55
class GlueAdaptor(glue: Glue) {
66

7-
def addDefinition(registry: ScalaDslRegistry): Unit = {
8-
registry.stepDefinitions.foreach(glue.addStepDefinition)
9-
registry.beforeHooks.foreach(glue.addBeforeHook)
10-
registry.afterHooks.foreach(glue.addAfterHook)
11-
registry.afterStepHooks.foreach(glue.addAfterStepHook)
12-
registry.beforeStepHooks.foreach(glue.addBeforeStepHook)
7+
/**
8+
* Load the step definitions and hooks from a ScalaDsl instance into the glue.
9+
*
10+
* @param registry ScalaDsl instance registry
11+
* @param scenarioScoped true for class instances, false for object singletons
12+
*/
13+
def loadRegistry(registry: ScalaDslRegistry, scenarioScoped: Boolean): Unit = {
14+
registry.stepDefinitions.map(ScalaStepDefinition(_, scenarioScoped)).foreach(glue.addStepDefinition)
15+
registry.beforeHooks.map(ScalaHookDefinition(_, scenarioScoped)).foreach(glue.addBeforeHook)
16+
registry.afterHooks.map(ScalaHookDefinition(_, scenarioScoped)).foreach(glue.addAfterHook)
17+
registry.afterStepHooks.map(ScalaHookDefinition(_, scenarioScoped)).foreach(glue.addAfterStepHook)
18+
registry.beforeStepHooks.map(ScalaHookDefinition(_, scenarioScoped)).foreach(glue.addBeforeStepHook)
1319
}
1420

1521
}

scala/sources/src/main/scala/io/cucumber/scala/ScalaBackend.scala

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,53 +11,58 @@ import io.cucumber.core.resource.{ClasspathScanner, ClasspathSupport}
1111
import scala.collection.JavaConverters._
1212
import scala.util.Try
1313

14-
class ScalaBackend(classLoaderProvider: Supplier[ClassLoader]) extends Backend {
14+
class ScalaBackend(lookup: Lookup, container: Container, classLoaderProvider: Supplier[ClassLoader]) extends Backend {
1515

1616
private val classFinder = new ClasspathScanner(classLoaderProvider)
1717

18-
private[scala] var scalaGlueInstances: Seq[ScalaDsl] = Nil
18+
private var glueAdaptor: GlueAdaptor = _
19+
private[scala] var scalaGlueClasses: Seq[Class[_ <: ScalaDsl]] = Nil
1920

2021
override def disposeWorld(): Unit = {
21-
scalaGlueInstances = Nil
22+
// Nothing to do
2223
}
2324

2425
override def getSnippet(): Snippet = {
2526
new ScalaSnippet()
2627
}
2728

2829
override def buildWorld(): Unit = {
29-
// Nothing to do
30+
// Instantiate all the glue classes and load the glue code from them
31+
scalaGlueClasses.foreach { glueClass =>
32+
val glueInstance = lookup.getInstance(glueClass)
33+
glueAdaptor.loadRegistry(glueInstance.registry, scenarioScoped = true)
34+
}
3035
}
3136

3237
override def loadGlue(glue: Glue, gluePaths: JList[URI]): Unit = {
3338

39+
glueAdaptor = new GlueAdaptor(glue)
40+
3441
val dslClasses = gluePaths.asScala
3542
.filter(gluePath => ClasspathSupport.CLASSPATH_SCHEME.equals(gluePath.getScheme))
3643
.map(ClasspathSupport.packageName)
3744
.flatMap(basePackageName => classFinder.scanForSubClassesInPackage(basePackageName, classOf[ScalaDsl]).asScala)
3845
.filter(glueClass => !glueClass.isInterface)
39-
.filter(glueClass => glueClass.getConstructors.length > 0)
4046

4147
val (clsClasses, objClasses) = dslClasses.partition(isRegularClass)
4248

49+
// Retrieve Scala objects (singletons)
4350
val objInstances = objClasses.map { cls =>
4451
val instField = cls.getDeclaredField("MODULE$")
4552
instField.setAccessible(true)
4653
instField.get(null).asInstanceOf[ScalaDsl]
4754
}
48-
val clsInstances = clsClasses.map {
49-
_.newInstance()
50-
}
51-
52-
// FIXME we should not create instances above but fill the container like Cucumber Java does
53-
// https://github.com/cucumber/cucumber-jvm-scala/issues/1
54-
//clsClasses.foreach(container.addClass(_))
55-
scalaGlueInstances = objInstances.toSeq ++ clsInstances
5655

57-
val glueAdaptor = new GlueAdaptor(glue)
56+
// Regular Scala classes are added to the container, they will be instantiated by the container depending on its logic
57+
// Object are not because by definition they are singletons
58+
clsClasses.foreach { glueClass =>
59+
container.addClass(glueClass)
60+
scalaGlueClasses = scalaGlueClasses :+ glueClass
61+
}
5862

59-
scalaGlueInstances.foreach { glueInstance =>
60-
glueAdaptor.addDefinition(glueInstance.registry)
63+
// For object, we add the definitions here, once for all
64+
objInstances.foreach { glueInstance =>
65+
glueAdaptor.loadRegistry(glueInstance.registry, scenarioScoped = false)
6166
}
6267

6368
()

scala/sources/src/main/scala/io/cucumber/scala/ScalaBackendProviderService.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import io.cucumber.core.backend.{Backend, BackendProviderService, Container, Loo
77
class ScalaBackendProviderService extends BackendProviderService {
88

99
override def create(lookup: Lookup, container: Container, classLoaderSupplier: Supplier[ClassLoader]): Backend = {
10-
new ScalaBackend(classLoaderSupplier)
10+
new ScalaBackend(lookup, container, classLoaderSupplier)
1111
}
1212

1313
}

0 commit comments

Comments
 (0)