Skip to content

Commit 0fbc9a0

Browse files
committed
Project restucturing to support both a plugin that executes cucumber and tight integration into the sbt test phase.
- separated the project into two parts: plugin and integration - started a skeleton implementation of the test integration framework - added the ability for the plugin to add test integration to a project by including an alternative set of features - created a new test project to test the tight integration to the test phase - upped all the version numbers to 0.7.0
1 parent 76a3d7a commit 0fbc9a0

File tree

25 files changed

+442
-252
lines changed

25 files changed

+442
-252
lines changed

Diff for: .ensime

+144-82
Large diffs are not rendered by default.

Diff for: README.markdown

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

8383
resolvers += "Templemore Repository" at "http://templemore.co.uk/repo"
8484

85-
addSbtPlugin("templemore" % "xsbt-cucumber-plugin" % "0.6.2")
85+
addSbtPlugin("templemore" % "xsbt-cucumber-plugin" % "0.7.0")
8686

8787
### Basic Configuration ###
8888
To add the cucumber plugin settings to a basic project, just add the following to the build.sbt file:
@@ -148,6 +148,10 @@ Requests for features can be posted to the issues list or emailed to the author.
148148

149149
## Release History ##
150150

151+
### 0.7.0 ###
152+
153+
TODO
154+
151155
### 0.6.2 ###
152156
Upgrade to cucumber-jvm version 1.1.1 to allow compatibility with Scala 2.10.0-RC1 release.
153157

Diff for: build.sbt

-14
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package templemore.sbt.cucumber
2+
3+
import org.scalatools.testing._
4+
5+
/**
6+
* Framework implementation that allows cucumber to be run as part of sbt's
7+
* standard 'test' phase.
8+
*
9+
* @author Chris Turner
10+
*/
11+
class CucumberFramework extends Framework {
12+
val name = "cucumber"
13+
val tests = Array[Fingerprint](CucumberRunOnceFingerprint)
14+
15+
def testRunner(testClassLoader: ClassLoader, loggers: Array[Logger]) = {
16+
//TODO
17+
println("%%% Creating a new runner...")
18+
new CucumberRunner()
19+
}
20+
}
21+
22+
class CucumberRunner extends Runner2 {
23+
def run(testClassName: String, fingerprint: Fingerprint, eventHandler: EventHandler, args: Array[String]) = {
24+
println("%%% CUCUMBER RUNNER")
25+
println("%%% Running test class: " + testClassName)
26+
println("%%% with fingerprint: " + fingerprint)
27+
println("%%% with eventHandler: " + eventHandler)
28+
println("%%% with args: " + args.mkString(", "))
29+
//TODO
30+
}
31+
}
32+
33+
object CucumberRunOnceFingerprint extends SubclassFingerprint {
34+
val isModule = false
35+
val superClassName = classOf[RunCucumber].getName
36+
}
37+
38+
trait RunCucumber

Diff for: src/main/scala/templemore/xsbt/cucumber/CucumberPlugin.scala renamed to plugin/src/main/scala/templemore/sbt/cucumber/CucumberPlugin.scala

+25-22
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,25 @@
1-
package templemore.xsbt.cucumber
1+
package templemore.sbt.cucumber
22

33
import sbt._
44
import Keys._
55
import Project.Initialize
6+
import templemore.sbt.util._
67

78
/**
89
* @author Chris Turner
910
*/
10-
object CucumberPlugin extends Plugin with CucumberIntegration {
11+
object CucumberPlugin extends Plugin with Integration {
1112

13+
private val projectVersion = "0.7.0"
1214
private val CucumberVersionForScala2_9 = "1.0.9"
1315
private val CucumberVersionForScala2_10 = "1.1.1"
1416

1517
type LifecycleCallback = () => Unit
1618

1719
val cucumber = InputKey[Int]("cucumber")
18-
val cucumberTestSettings = TaskKey[CucumberSettings]("cucumber-settings")
19-
val cucumberOptions = TaskKey[CucumberOptions]("cucumber-options")
20-
val cucumberOutput = TaskKey[CucumberOutput]("cucumber-output")
20+
val cucumberTestSettings = TaskKey[JvmSettings]("cucumber-settings")
21+
val cucumberOptions = TaskKey[Options]("cucumber-options")
22+
val cucumberOutput = TaskKey[Output]("cucumber-output")
2123

2224
val cucumberMaxMemory = SettingKey[String]("cucumber-max-memory")
2325
val cucumberMaxPermGen = SettingKey[String]("cucumber-max-perm-gen")
@@ -43,28 +45,22 @@ object CucumberPlugin extends Plugin with CucumberIntegration {
4345
val cucumberAfter = SettingKey[LifecycleCallback]("cucumber-after")
4446

4547
protected def cucumberTask(argTask: TaskKey[Seq[String]]) =
46-
(argTask, cucumberTestSettings, cucumberOptions, cucumberOutput, streams) map(testWithCucumber)
48+
(argTask, cucumberTestSettings, cucumberOptions, cucumberOutput, streams) map(cuke)
4749

48-
protected def cucumberSettingsTask: Initialize[Task[CucumberSettings]] =
49-
(cucumberMaxMemory, cucumberMaxPermGen, cucumberSystemProperties, cucumberJVMOptions, fullClasspath in Test, streams) map {
50-
(mm, mpg, sp, jvmopt, cp, s) => {
51-
CucumberSettings(mm, mpg, sp, jvmopt, cp.toList.map(_.data), LoggedOutput(s.log))
52-
}
50+
protected def cucumberSettingsTask: Initialize[Task[JvmSettings]] =
51+
(fullClasspath in Test, cucumberMainClass, streams, cucumberSystemProperties, cucumberJVMOptions, cucumberMaxMemory, cucumberMaxPermGen) map {
52+
(cp, mc, s, sp, jvmopt, mm, mpg) => JvmSettings(cp.toList.map(_.data), mc, LoggedOutput(s.log), sp, jvmopt, Some(mm), Some(mpg))
5353
}
5454

55-
protected def cucumberOptionsTask: Initialize[Task[CucumberOptions]] =
56-
(cucumberMainClass, cucumberFeaturesDir, cucumberStepsBasePackage, cucumberExtraOptions,
57-
cucumberBefore, cucumberAfter) map {
58-
(mc, fd, bp, o, bf, af) => {
59-
CucumberOptions(mc, fd, bp, o, bf, af)
60-
}
61-
}
55+
protected def cucumberOptionsTask: Initialize[Task[Options]] =
56+
(cucumberFeaturesDir, cucumberStepsBasePackage, cucumberExtraOptions,
57+
cucumberBefore, cucumberAfter) map ((fd, bp, o, bf, af) => Options(fd, bp, o, bf, af))
6258

63-
protected def cucumberOutputTask: Initialize[Task[CucumberOutput]] =
59+
protected def cucumberOutputTask: Initialize[Task[Output]] =
6460
(cucumberPrettyReport, cucumberHtmlReport, cucumberJunitReport, cucumberJsonReport,
6561
cucumberPrettyReportFile, cucumberHtmlReportDir, cucumberJunitReportFile, cucumberJsonReportFile) map {
6662
(pR, hR, juR, jsR, pRF, hRD, juRF, jsRF) => {
67-
CucumberOutput(pR, hR, juR, jsR, pRF, hRD, juRF, jsRF)
63+
Output(pR, hR, juR, jsR, pRF, hRD, juRF, jsRF)
6864
}
6965
}
7066

@@ -77,7 +73,7 @@ object CucumberPlugin extends Plugin with CucumberIntegration {
7773
private def cucumberMain(scalaVersion: String) =
7874
if ( scalaVersion.startsWith("2.10") ) "cucumber.api.cli.Main" else "cucumber.cli.Main"
7975

80-
val cucumberSettings = Seq(
76+
val cucumberSettings: Seq[Setting[_]] = Seq(
8177
libraryDependencies <+= scalaVersion { sv =>
8278
"info.cukes" % "cucumber-scala" % cucumberVersion(sv) % "test"
8379
},
@@ -110,4 +106,11 @@ object CucumberPlugin extends Plugin with CucumberIntegration {
110106
cucumberBefore := defaultBefore,
111107
cucumberAfter := defaultAfter
112108
)
113-
}
109+
110+
val cucumberSettingsWithTestPhaseIntegration = cucumberSettings ++ Seq(
111+
libraryDependencies ++= Seq(
112+
"templemore" %% "sbt-cucumber-integration" % projectVersion % "test"
113+
),
114+
testFrameworks += new TestFramework("templemore.sbt.cucumber.CucumberFramework")
115+
)
116+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package templemore.sbt.cucumber
2+
3+
import sbt._
4+
import std.TaskStreams
5+
import templemore.sbt.util._
6+
7+
/**
8+
* Provides the actual integration with cucumber jvm. Capable of launching
9+
* cucumber as both a forked JVM and within the current JVM process.
10+
*
11+
* @author Chris Turner
12+
*/
13+
trait Integration {
14+
15+
protected def cuke(args: Seq[String],
16+
jvmSettings: JvmSettings,
17+
options: Options,
18+
output: Output,
19+
s: TaskStreams[_]) = {
20+
val log = s.log
21+
22+
if ( options.featuresPresent ) {
23+
log.debug("JVM Settings: %s".format(jvmSettings))
24+
log.debug("Cucumber Options: %s".format(options))
25+
log.debug("Cucumber Output: %s".format(output))
26+
27+
runCucumber(args, jvmSettings, options, output, log)
28+
}
29+
else {
30+
log.info("No features directory found. Skipping for curent project.")
31+
0
32+
}
33+
}
34+
35+
private def runCucumber(args: Seq[String],
36+
jvmSettings: JvmSettings,
37+
options: Options,
38+
output: Output,
39+
log: Logger) = {
40+
def tagsFromArgs = args.filter(isATag).toList
41+
def namesFromArgs = args.filter(isNotATag).toList
42+
43+
def isATag(arg: String) = arg.startsWith("@") || arg.startsWith("~")
44+
def isNotATag(arg: String) = !isATag(arg)
45+
46+
log.info("Running cucumber...")
47+
options.beforeFunc()
48+
val result = launchCucumberInSeparateJvm(jvmSettings, options, output, tagsFromArgs, namesFromArgs)
49+
options.afterFunc()
50+
result
51+
}
52+
53+
private def launchCucumberInSeparateJvm(jvmSettings: JvmSettings,
54+
options: Options,
55+
output: Output,
56+
tags: List[String],
57+
names: List[String]): Int = {
58+
def makeOptionsList(options: List[String], flag: String) = options flatMap(List(flag, _))
59+
60+
val cucumberParams = ("--glue" :: options.basePackage :: Nil) ++
61+
options.extraOptions ++
62+
output.options ++
63+
makeOptionsList(tags, "--tags") ++
64+
makeOptionsList(names, "--name") ++
65+
(options.featuresDir.getPath :: Nil)
66+
JvmLauncher(jvmSettings).launch(cucumberParams)
67+
}
68+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package templemore.sbt.cucumber
2+
3+
import java.io.File
4+
5+
/**
6+
* The options to pass to cucumber.
7+
*
8+
* @author Chris Turner
9+
*/
10+
case class Options(featuresDir: File,
11+
basePackage: String,
12+
extraOptions: List[String],
13+
beforeFunc: () => Unit,
14+
afterFunc: () => Unit) {
15+
16+
def featuresPresent = featuresDir.exists
17+
}

Diff for: src/main/scala/templemore/xsbt/cucumber/CucumberOutput.scala renamed to plugin/src/main/scala/templemore/sbt/cucumber/Output.scala

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
package templemore.xsbt.cucumber
1+
package templemore.sbt.cucumber
22

33
import java.io.File
44

55
/**
6+
* Defines the output options for running cucumber.
7+
*
68
* @author Chris Turner
79
*/
8-
case class CucumberOutput(prettyReport: Boolean, htmlReport: Boolean, junitReport: Boolean, jsonReport: Boolean,
9-
prettyReportFile: File, htmlReportDir: File, junitReportFile: File, jsonReportFile: File) {
10+
case class Output(prettyReport: Boolean, htmlReport: Boolean, junitReport: Boolean, jsonReport: Boolean,
11+
prettyReportFile: File, htmlReportDir: File, junitReportFile: File, jsonReportFile: File) {
1012

1113
def options: List[String] = {
1214
(if (prettyReport) {
@@ -27,4 +29,4 @@ case class CucumberOutput(prettyReport: Boolean, htmlReport: Boolean, junitRepor
2729
"--format" :: "json-pretty:%s".format(jsonReportFile.getPath) :: Nil
2830
} else Nil)
2931
}
30-
}
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package templemore.sbt.util
2+
3+
import sbt._
4+
import java.io.File
5+
import sbt.OutputStrategy
6+
7+
/**
8+
* Launches a new JVM with the given options.
9+
*
10+
* @author Chris Turner
11+
*/
12+
case class JvmLauncher(settings: JvmSettings) {
13+
14+
private val maxMemory = settings.overrideMaxMemory.getOrElse("256M")
15+
private val maxPermGen = settings.overrideMaxPermGen.getOrElse("64M")
16+
17+
private val jvmArgs = ("-classpath" :: makeClasspath ::
18+
("-Xmx%s" format maxMemory) :: ("-XX:MaxPermSize=%s" format maxPermGen) :: Nil) ++
19+
makeSystemProperties ++ settings.jvmOptions
20+
21+
def launch(params: List[String]): Int = {
22+
val args = jvmArgs ++ (settings.mainClass :: params)
23+
settings.outputStrategy.asInstanceOf[LoggedOutput].logger.debug(args mkString " ")
24+
Fork.java(None, args, None, Map.empty[String, String], settings.outputStrategy)
25+
}
26+
27+
protected def makeClasspath = settings.classpath map (_.getPath) mkString File.pathSeparator
28+
protected def makeSystemProperties = settings.systemProperties.toList map (entry => "-D%s=%s".format(entry._1, entry._2))
29+
}
30+
31+
/**
32+
* The options to pass to the JVM.
33+
*
34+
* @author Chris Turner
35+
*/
36+
case class JvmSettings(classpath: List[File],
37+
mainClass: String,
38+
outputStrategy: OutputStrategy,
39+
systemProperties: Map[String, String],
40+
jvmOptions: List[String],
41+
overrideMaxMemory: Option[String],
42+
overrideMaxPermGen: Option[String])

Diff for: project/Build.scala

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import sbt._
2+
import Keys._
3+
4+
object Settings {
5+
val buildOrganization = "templemore"
6+
val buildScalaVersion = "2.9.2"
7+
val buildVersion = "0.7.0"
8+
9+
val buildSettings = Defaults.defaultSettings ++
10+
Seq (organization := buildOrganization,
11+
scalaVersion := buildScalaVersion,
12+
version := buildVersion,
13+
scalacOptions += "-deprecation")
14+
}
15+
16+
object Dependencies {
17+
val testInterface = "org.scala-tools.testing" % "test-interface" % "0.5"
18+
}
19+
20+
object Build extends Build {
21+
import Dependencies._
22+
import Settings._
23+
24+
lazy val parentProject = Project("sbt-cucumber-parent", file ("."),
25+
settings = buildSettings) aggregate (pluginProject, integrationProject)
26+
27+
lazy val pluginProject = Project("sbt-cucumber-plugin", file ("plugin"),
28+
settings = buildSettings ++
29+
Seq(sbtPlugin := true))
30+
31+
lazy val integrationProject = Project ("sbt-cucumber-integration", file ("integration"),
32+
settings = buildSettings ++
33+
Seq(crossScalaVersions := Seq("2.9.2", "2.10.0-RC1"),
34+
libraryDependencies ++= Seq(testInterface)))
35+
}

Diff for: src/main/scala/templemore/xsbt/cucumber/Cucumber.scala

-40
This file was deleted.

0 commit comments

Comments
 (0)