Skip to content
This repository was archived by the owner on Apr 29, 2024. It is now read-only.

Commit 35ccfe4

Browse files
authored
Merge pull request #87 from Tapad/feature/specs2
Feature/specs2
2 parents beca52c + 372fd7f commit 35ccfe4

File tree

11 files changed

+183
-3
lines changed

11 files changed

+183
-3
lines changed

README.md

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ plugin will attempt to locate it in one of three places with the precedence orde
7272
testDependenciesClasspath := // The path to all managed and unmanaged Test and Compile dependencies. This path needs to include the ScalaTest Jar for the tests to execute. This defaults to all managedClasspath and unmanagedClasspath in the Test and fullClasspath in the Compile Scope.
7373
testCasesJar := // The path to the Jar file containing the tests to execute. This defaults to the Jar file with the tests from the current sbt project.
7474
testCasesPackageTask := // The sbt Task to package the test cases used when running 'dockerComposeTest'. This defaults to the 'packageBin' task in the 'Test' Scope.
75+
testPassUseSpecs2 := // True if Specs2 is to be used to execute the test pass. This defaults to False and ScalaTest is used.
7576
variablesForSubstitution := // A Map[String,String] of variables to substitute in your docker-compose file. These are substituted substituted by the plugin and not using environment variables.
7677
variablesForSubstitutionTask := // An sbt task that returns a Map[String,String] of variables to substitute in your docker-compose file. These are substituted by the plugin and not using environment variables.
7778
```
@@ -346,7 +347,19 @@ docker-compose.yml:
346347
6) [**basic-with-tests-integration**](examples/basic-with-tests-integration): This project shows how to change the default sbt Scope of the
347348
tests being executed from 'Test' to 'IntegrationTest' when 'dockerComposeTest' is run.
348349
349-
350+
7) [**basic-with-tests-spec2**](examples/basic-with-tests-specs2): This project shows how to execute Specs2 based test cases via the 'specs2.files' runner
351+
by setting the following property in build.sbt:
352+
353+
```
354+
testPassUseSpecs2 := true
355+
```
356+
357+
Additionally, you can override the default Specs2 file runner properties as follows:
358+
359+
```
360+
testExecutionExtraConfigTask := Map("filesrunner.verbose" -> s"true")
361+
```
362+
350363
Currently Unsupported Docker Compose Fields
351364
-------------------------------------------
352365
1) "build:" - All docker compose services need to specify an "image:" field.
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name := "basic"
2+
3+
version := "1.0.0"
4+
5+
scalaVersion := "2.11.1"
6+
7+
libraryDependencies ++= Seq("org.specs2" %% "specs2-core" % "3.7.3" % "test",
8+
"org.scalaj" %% "scalaj-http" % "2.2.1" % "test",
9+
"org.pegdown" % "pegdown" % "1.6.0" % "test"
10+
)
11+
12+
enablePlugins(DockerPlugin, DockerComposePlugin)
13+
14+
//Set the image creation Task to be the one used by sbt-docker
15+
dockerImageCreationTask := docker.value
16+
17+
testPassUseSpecs2 := true
18+
19+
testExecutionExtraConfigTask := Map("filesrunner.verbose" -> s"true")
20+
21+
dockerfile in docker := {
22+
new Dockerfile {
23+
val dockerAppPath = "/app/"
24+
val mainClassString = (mainClass in Compile).value.get
25+
val classpath = (fullClasspath in Compile).value
26+
from("java")
27+
add(classpath.files, dockerAppPath)
28+
entryPoint("java", "-cp", s"$dockerAppPath:$dockerAppPath/*", s"$mainClassString")
29+
}
30+
}
31+
32+
imageNames in docker := Seq(ImageName(
33+
repository = name.value.toLowerCase,
34+
tag = Some(version.value))
35+
)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
basic:
2+
image: basic:1.0.0
3+
environment:
4+
JAVA_TOOL_OPTIONS: -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005
5+
ports:
6+
- "0:8080"
7+
- "0:5005"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
sbt.version=0.13.16
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
addSbtPlugin("se.marcuslonnberg" % "sbt-docker" % "1.4.1")
2+
3+
resolvers += "Sonatype Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots/"
4+
5+
addSbtPlugin("com.tapad" % "sbt-docker-compose" % "1.0.26-SNAPSHOT")
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import java.io.PrintWriter
2+
import java.net.ServerSocket
3+
4+
object BasicApp extends App {
5+
6+
val text =
7+
"""HTTP/1.0 200 OK
8+
Content-Type: text/html
9+
Content-Length: 200
10+
11+
<HTML> <HEAD> <TITLE>Hello, World!</TITLE> </HEAD> <BODY LANG="en-US" BGCOLOR="#e6e6ff" DIR="LTR"> <P ALIGN="CENTER"> <FONT FACE="Arial, sans-serif" SIZE="6">Hello, World!</FONT> </P> </BODY> </HTML>"""
12+
val port = 8080
13+
val listener = new ServerSocket(port)
14+
15+
while (true) {
16+
val sock = listener.accept()
17+
new PrintWriter(sock.getOutputStream, true).println(text)
18+
sock.shutdownOutput()
19+
}
20+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import scala.Console._
2+
import scala.sys.process._
3+
import org.specs2._
4+
import org.specs2.execute._
5+
import scalaj.http.Http
6+
import java.io.{ByteArrayOutputStream, PrintWriter}
7+
8+
class BasicAppSpec extends mutable.Specification {
9+
10+
// The System Properties will contain the connection information for the running Docker Compose
11+
// services. The key into the map is "serviceName:containerPort" and it will return "host:hostPort" which is the
12+
// Docker Compose generated endpoint that can be connected to at runtime. You can use this to endpoint connect to
13+
// for testing. Each service will also inject a "serviceName:containerId" key with the value equal to the container id.
14+
// You can use this to emulate service failures by killing and restarting the container.
15+
val basicServiceName = "basic"
16+
val basicServiceHostKey = s"$basicServiceName:8080"
17+
val basicServiceContainerIdKey = s"$basicServiceName:containerId"
18+
val hostInfo = getHostInfo()
19+
val containerId = getContainerId()
20+
21+
"Validate that the Docker Compose endpoint returns a success code and the string 'Hello, World!'" >> {
22+
println(s"Attempting to connect to: $hostInfo, container id is $containerId")
23+
24+
eventually {
25+
val output = Http(s"http://$hostInfo").asString
26+
output.isSuccess mustEqual true
27+
output.body must contain ("Hello, World!")
28+
}
29+
}
30+
31+
def getHostInfo(): String = getContainerSetting(basicServiceHostKey)
32+
def getContainerId(): String = getContainerSetting(basicServiceContainerIdKey)
33+
34+
def getContainerSetting(key: String): String = {
35+
if (System.getProperty(key) != null) {
36+
System.getProperty(key)
37+
}
38+
else {
39+
throw new FailureException(Failure(s"Cannot find the expected Docker Compose service key '$key' in the System Properties"))
40+
}
41+
}
42+
}

src/main/scala/com/tapad/docker/ComposeTestRunner.scala

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,4 +102,54 @@ trait ComposeTestRunner extends SettingsHelper with PrintFormatting {
102102
state
103103
}
104104
}
105+
106+
/**
107+
* Build up a set of parameters to pass as System Properties that can be accessed from Specs2
108+
* Compiles and binPackages the latest Test code.
109+
* Starts a test pass using the Specs2 Files Runner
110+
* @param state The sbt state
111+
* @param args The command line arguments
112+
* @param instance The running Docker Compose instance to test against
113+
*/
114+
def runTestPassSpecs2(implicit state: State, args: Seq[String], instance: Option[RunningInstanceInfo]): State = {
115+
//Build the list of Docker Compose connection endpoints to pass as System Properties
116+
//format: <-Dservice:containerPort=host:hostPort>
117+
val testParams = instance match {
118+
case Some(inst) => inst.servicesInfo.flatMap(service =>
119+
service.ports.map(port =>
120+
s"-D${service.serviceName}:${port.containerPort}=${service.containerHost}:${port.hostPort}")
121+
:+ s"-D${service.serviceName}:containerId=${service.containerId}").mkString(" ")
122+
case None => ""
123+
}
124+
125+
val extraTestParams = runTestExecutionExtraConfigTask(state).map { case (k, v) => s"-D$k=$v" }
126+
127+
print("Compiling and Packaging test cases...")
128+
binPackageTests
129+
130+
// Looks for the <-debug:port> argument and will suspend test case execution until a debugger is attached
131+
val debugSettings = getArgValue(testDebugPortArg, args) match {
132+
case Some(port) => s"-J-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=$port"
133+
case None => ""
134+
}
135+
136+
val suppressColor = getSetting(suppressColorFormatting)
137+
val noColorOption = if (suppressColor) "-Dspecs2.color=false" else ""
138+
val testArgs = getSetting(testExecutionArgs).split(" ").toSeq
139+
val testDependencies = getTestDependenciesClassPath
140+
if (testDependencies.matches(".*org.specs2.*")) {
141+
val testParamsList = testParams.split(" ").toSeq ++ extraTestParams
142+
val testRunnerCommand = (Seq("java", debugSettings, noColorOption) ++
143+
testParamsList ++
144+
Seq("-cp", testDependencies, "org.specs2.runner.files") ++
145+
testArgs ++
146+
testParamsList).filter(_.nonEmpty)
147+
if (testRunnerCommand.! == 0) state
148+
else state.fail
149+
} else {
150+
printBold("Cannot find a Specs2 Jar dependency. Please make sure it is added to your sbt projects " +
151+
"libraryDependencies.", suppressColor)
152+
state
153+
}
154+
}
105155
}

src/main/scala/com/tapad/docker/DockerComposeKeys.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ trait DockerComposeKeysLocal {
2222
val testCasesJar = settingKey[String]("The path to the Jar file containing the tests to execute. This defaults to the Jar file with the tests from the current sbt project.")
2323
val testCasesPackageTask = taskKey[File]("The sbt TaskKey to package the test cases used when running 'dockerComposeTest'. This defaults to the 'packageBin' task in the 'Test' Scope.")
2424
val testDependenciesClasspath = taskKey[String]("The path to all managed and unmanaged Test and Compile dependencies. This path needs to include the ScalaTest Jar for the tests to execute. This defaults to all managedClasspath and unmanagedClasspath in the Test and fullClasspath in the Compile Scope.")
25+
val testPassUseSpecs2 = settingKey[Boolean]("True if Specs2 is to be used to execute the test pass. This defaults to False and ScalaTest is used.")
2526
val runningInstances = AttributeKey[List[RunningInstanceInfo]]("For Internal Use: Contains information on the set of running Docker Compose instances.")
2627
val variablesForSubstitution = settingKey[Map[String, String]]("A Map[String,String] of variables to substitute in your docker-compose file. These are substituted by the plugin and not using environment variables.")
2728
val variablesForSubstitutionTask = taskKey[Map[String, String]]("An sbt task that returns a Map[String,String] of variables to substitute in your docker-compose file. These are substituted by the plugin and not using environment variables.")

src/main/scala/com/tapad/docker/DockerComposePlugin.scala

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ object DockerComposePlugin extends DockerComposePluginLocal {
7575
val testExecutionExtraConfigTask = DockerComposeKeys.testExecutionExtraConfigTask
7676
val testExecutionArgs = DockerComposeKeys.testExecutionArgs
7777
val testCasesJar = DockerComposeKeys.testCasesJar
78+
val testPassUseSpecs2 = DockerComposeKeys.testPassUseSpecs2
7879
val suppressColorFormatting = DockerComposeKeys.suppressColorFormatting
7980
val scalaTestJar = DockerComposeKeys.testDependenciesClasspath
8081
val variablesForSubstitution = DockerComposeKeys.variablesForSubstitution
@@ -418,7 +419,10 @@ class DockerComposePluginLocal extends AutoPlugin with ComposeFile with DockerCo
418419
val requiresShutdown = getMatchingRunningInstance(newState, args).isEmpty
419420
val (preTestState, instance) = getTestPassInstance(newState, args)
420421

421-
val finalState = runTestPass(preTestState, args, instance)
422+
val finalState = if (getSetting(testPassUseSpecs2))
423+
runTestPassSpecs2(preTestState, args, instance)
424+
else
425+
runTestPass(preTestState, args, instance)
422426

423427
if (requiresShutdown)
424428
stopDockerCompose(finalState, Seq(instance.get.instanceName))

0 commit comments

Comments
 (0)