Skip to content

Commit 6fa47a9

Browse files
committed
Corrected an error in the documentation
Refined the launcher to access cucumber apis at a lower level that avoids the problem of System.out being closed by the pretty formatter.
1 parent 1e813bf commit 6fa47a9

File tree

2 files changed

+57
-13
lines changed

2 files changed

+57
-13
lines changed

README.markdown

+1-1
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ To install the cucumber plugin, add entries to the build plugins file (project/p
109109

110110
resolvers += "Templemore Repository" at "http://templemore.co.uk/repo"
111111

112-
addSbtPlugin("templemore" % "xsbt-cucumber-plugin" % "0.7.0")
112+
addSbtPlugin("templemore" % "sbt-cucumber-plugin" % "0.7.0")
113113

114114
### Basic Configuration ###
115115
To add the cucumber plugin settings to a basic project, just add the following to the build.sbt file:

integration/src/main/scala/templemore/sbt/cucumber/ReflectingCucumberLauncher.scala

+56-12
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
package templemore.sbt.cucumber
22

3+
import scala.collection.JavaConverters._
34
import java.lang.reflect.InvocationTargetException
45
import java.util.Properties
6+
import java.io.PrintStream
57

68
class ReflectingCucumberLauncher(debug: (String) => Unit, error: (String) => Unit) {
79

810
private val RuntimeOptionsClassName = "cucumber.runtime.RuntimeOptions"
911
private val MultiLoaderClassName = "cucumber.runtime.io.MultiLoader"
1012
private val MultiLoaderClassName_1_0_9 = "cucumber.io.MultiLoader"
1113
private val RuntimeClassName = "cucumber.runtime.Runtime"
14+
private val FormatterClassName = "gherkin.formatter.Formatter"
15+
private val ReporterClassName = "gherkin.formatter.Reporter"
16+
private val SummaryPrinterClassName = "cucumber.runtime.snippets.SummaryPrinter"
1217

1318
def apply(cucumberArguments: Array[String],
1419
testClassLoader: ClassLoader): Int = {
@@ -17,13 +22,12 @@ class ReflectingCucumberLauncher(debug: (String) => Unit, error: (String) => Uni
1722
runCucumber(runtime).asInstanceOf[Byte].intValue
1823
}
1924

20-
private def runCucumber(runtime: AnyRef) = try {
21-
val runtimeClass = runtime.getClass
22-
runtimeClass.getMethod("writeStepdefsJson").invoke(runtime)
23-
println("*** about to call run()...")
24-
runtimeClass.getMethod("run").invoke(runtime)
25-
println("*** run() complete, getting exit status...")
26-
runtimeClass.getMethod("exitStatus").invoke(runtime)
25+
26+
private def runCucumber(runtime: CucumberRuntime) = try {
27+
runtime.initialise
28+
runtime.run
29+
runtime.printSummary
30+
runtime.exitStatus
2731
} catch {
2832
case e: InvocationTargetException => {
2933
val cause = if ( e.getCause == null ) e else e.getCause
@@ -32,17 +36,54 @@ println("*** run() complete, getting exit status...")
3236
}
3337
}
3438

39+
case class CucumberRuntime(runtime: AnyRef, options: AnyRef, loader: AnyRef,
40+
formatter: AnyRef, reporter: AnyRef, summaryPrinter: AnyRef,
41+
classLoader: ClassLoader, formatterClass: Class[_], reporterClass: Class[_]) {
42+
private val runtimeClass = runtime.getClass
43+
private val optionsClass = options.getClass
44+
private val loaderClass = loader.getClass.getInterfaces()(0)
45+
private val summaryPrinterClass = summaryPrinter.getClass
46+
47+
def initialise = runtimeClass.getMethod("writeStepdefsJson").invoke(runtime)
48+
def printSummary = summaryPrinterClass.getMethod("print", runtimeClass).invoke(summaryPrinter, runtime)
49+
def exitStatus = runtimeClass.getMethod("exitStatus").invoke(runtime)
50+
51+
def run = {
52+
val featureList = optionsClass.getMethod("cucumberFeatures", loaderClass).invoke(options, loader).asInstanceOf[java.util.List[Object]].asScala
53+
featureList foreach { feature =>
54+
val featureClass = feature.getClass
55+
featureClass.getMethod("run", formatterClass, reporterClass, runtimeClass).invoke(feature, formatter, reporter, runtime)
56+
}
57+
}
58+
}
59+
3560
private def buildRuntime(properties: Properties,
3661
arguments: Array[String],
37-
classLoader: ClassLoader): AnyRef = {
62+
classLoader: ClassLoader): CucumberRuntime = try {
3863
def buildLoader(clazz: Class[_]) =
3964
clazz.getConstructor(classOf[ClassLoader]).newInstance(classLoader).asInstanceOf[AnyRef]
4065
def buildOptions(clazz: Class[_]) =
4166
clazz.getConstructor(classOf[Properties], classOf[Array[String]]).newInstance(properties.asInstanceOf[AnyRef], arguments).asInstanceOf[AnyRef]
4267

43-
val (runtimeClass, optionsClass, loaderClass) = loadCucumberClasses(classLoader)
68+
val (runtimeClass, optionsClass, loaderClass, formatterClass, reporterClass, summaryPrinterClass) = loadCucumberClasses(classLoader)
69+
70+
val options = buildOptions(optionsClass)
71+
val loader = buildLoader(loaderClass)
72+
73+
val formatter = optionsClass.getMethod("formatter", classOf[ClassLoader]).invoke(options, classLoader).asInstanceOf[AnyRef]
74+
val reporter = optionsClass.getMethod("reporter", classOf[ClassLoader]).invoke(options, classLoader).asInstanceOf[AnyRef]
75+
76+
val summaryPrinterConstructor = summaryPrinterClass.getConstructor(classOf[PrintStream])
77+
val summaryPrinter = summaryPrinterConstructor.newInstance(System.out).asInstanceOf[AnyRef]
78+
4479
val runtimeConstructor = runtimeClass.getConstructor(loaderClass.getInterfaces()(0), classOf[ClassLoader], optionsClass)
45-
runtimeConstructor.newInstance(buildLoader(loaderClass), classLoader, buildOptions(optionsClass)).asInstanceOf[AnyRef]
80+
val runtime = runtimeConstructor.newInstance(loader, classLoader, options).asInstanceOf[AnyRef]
81+
82+
CucumberRuntime(runtime, options, loader, formatter, reporter, summaryPrinter, classLoader, formatterClass, reporterClass)
83+
} catch {
84+
case e =>
85+
error("Unable to construct cucumber runtime. Please report this as an error. (Details: " + e.getMessage + ")")
86+
throw e
4687
}
4788

4889
private def loadCucumberClasses(classLoader: ClassLoader) = try {
@@ -54,10 +95,13 @@ println("*** run() complete, getting exit status...")
5495
val runtimeOptionsClass = classLoader.loadClass(RuntimeOptionsClassName)
5596
val multiLoaderClass = classLoader.loadClass(multiLoaderClassName)
5697
val runtimeClass = classLoader.loadClass(RuntimeClassName)
57-
(runtimeClass, runtimeOptionsClass, multiLoaderClass)
98+
val formatterClass = classLoader.loadClass(FormatterClassName)
99+
val reporterClass = classLoader.loadClass(ReporterClassName)
100+
val summaryPrinterClass = classLoader.loadClass(SummaryPrinterClassName)
101+
(runtimeClass, runtimeOptionsClass, multiLoaderClass, formatterClass, reporterClass, summaryPrinterClass)
58102
} catch {
59103
case e: ClassNotFoundException =>
60-
error("Unable to load Cucumber classes. Please check your project dependencied. (Details: " + e.getMessage + ")")
104+
error("Unable to load Cucumber classes. Please check your project dependencies. (Details: " + e.getMessage + ")")
61105
throw e
62106
}
63107

0 commit comments

Comments
 (0)