How to run subprojects tests (including setup methods) sequentially when testing - scala

EDIT4 Most of the text below the horizontal rule doesn't have anything to do with the real problem. At the beginning I thought forking is the problem, but that is not true.
I'm trying to run all the tests of an aggregates subprojects. Before all the tests in a subproject a setup method should run, and after the tests in a subproject a cleanup method should run.
When running the test task for the aggregate project, I expect the following sequence
setup method of project A is called
tests for project A are executed
cleanup method for project A is called
the same for project B
But the sequence is:
setup methods of project A and B are called
tests of project A are executed
tests of project B are executed
cleanup methods of project A and B are called
A build script having this behaviour can be found here.
How can I fix this problem to get my expected sequence?
I'm using forking to run the tests in my subprojects.
For every subproject a mongo db is started before the tests and stopped
after the tests.
The tests within one project run sequentially; this works well if I run the tests for a single project.
But if I run the task test for project root (the aggregate containing the sub projects),
I want the forked jvms to be started sequentially, i.e.
the jvm for project A is forked and its tests are executed
the jvm for project B is forked and its tests are executed
...
But it looks like the jvms are started in parallel; this is not what I want.
I tried the following (which should be set to 1 already, according to the documentation):
concurrentRestrictions in Test := Seq(
Tags.limit(Tags.ForkedTestGroup, 1)
)
But it didn't work. Directly after starting the test task, the following is
printed (before any test log is printed) from my setup method:
startupDb, thread name = pool-4-thread-5
startupDb, thread name = pool-4-thread-7
startupDb, thread name = pool-4-thread-2
startupDb, thread name = pool-4-thread-6
startupDb, thread name = pool-4-thread-8
startupDb, thread name = pool-4-thread-3
startupDb, thread name = pool-4-thread-9
These are my test related settings:
parallelExecution in Test := false,
testOptions in Test += Tests.Setup( () => MongoTest.startupDb() ),
testOptions in Test += Tests.Cleanup( () => MongoTest.shutdownDb() ),
fork in Test := true,
concurrentRestrictions in Test := Seq(
Tags.limit(Tags.ForkedTestGroup, 1)
)
It's important for me to use forking, but in the way as described above.
I'm using sbt 0.13.0 on Windows 7.
EDIT I created a gist with a sample build.
EDIT2 In the documentation it says:
Control the number of forked JVMs allowed to run at the same time by setting the limit on Tags.ForkedTestGroup tag, which is 1 by default
So this should work theoretically.
Is this a bug? If not, how can I manage to do that?
EDIT3 It looks like forking is not the problem. The problem is that the setup methods are called immediately for all subprojects.

Hmm. Trivial answer convert to comment. It sounds like you are asking the same question that got solved in the other question (see comments). The answer seems to be
parallelExecution in ThisBuild := false
and I found another blog post that suggested
parallelExecution in Global := false
to be the answer. The "Global" answer also suggested that would turn off parallel execution for things like compilation but that's probably less important than running tests.

As of sbt 0.12, you can make all test tasks in all subprojects run serially with this setting:
concurrentRestrictions in Global += Tags.limit(Tags.Test, 1)
(Tags.Test is a built in semantic tag for all test tasks).
See: Parallel-Execution in the SBT manual.

There is another way to prevent parallel execution. You can make the test tasks of the different projects depend on each other:
test in Project2 := (test in Project2).dependsOn(test in Project1).value
parallelExecution in Test in Project2 := false

Related

How to use a separate configuration file for tests in CI, using Play and sbt?

For a project based on Play with sbt, I'd like to have multiple flavors for test runs, using different configuration files. Motivation is being able to run tests either against a local or a remote database.
There's already a custom config file speicified for general test runs (in build.sbt):
javaOptions in Test += "-Dconfig.file=conf/application.test.conf"
Now I would like to have another command where the same tests run against some configuration file conf/application.test-ci.conf.
Approaches tried so far
Adding a command alias
addCommandAlias("test-ci", ";test -Dconfig.file=conf/application.test-ci.conf")
This fails with an error message of a missing semicolon (;), indicating that sbt interprets the resulting command line as multiple commands, but I don't understand why.
Extend Test
lazy val CITest = config("ci") extend Test
lazy val config = (project in file(".")).enablePlugins(PlayScala)
.configs(CITest)
.settings(inConfig(CITest)(Defaults.testTasks): _*)
.settings(
javaOptions in CITest += "-Dconfig.file=conf/application.test-ci.conf"
)
javaOptions in CITest -= "-Dconfig.file=conf/application.test.conf"
I'm don't fully understand what this is doing, but it always seems to pick up the other test configuration file.
How can I specify multiple test setups picking up different configuration files?
Try first applying the setting via set command and then follow up with test task like so
addCommandAlias(
"test-ci",
""";set Test/javaOptions ++= Seq("-Dconfig.file=conf/application.test.con"); test"""
)
Notice how ; separates the set from test.
Another approach is to modify the setting based on the environment. Usually there is some environment variable set on CI like CI or BUILD, so you can modify javaOptions conditionally (without any custom configurations):
Test/javaOptions ++= {
if (sys.env.get("CI").isEmpty) Seq.empty
else Seq("-Dconfig.file=conf/application.test-ci.conf")
}
Note: Test/javaOptions is the new syntax for javaOptions in Test (since sbt 1)

SBT how to publish an artifact generated from the run command

I'm attempting to compile and run scala code that generates a file in SBT (in this case its a swagger file).
the following sbt executes fine. The jar is built and executed and the swagger.zip file the execution creates is in the target directory. I can't, however, seem to get the zip file to get published to my artifactory repo like my standard jar files would.
Any idea on what I'm missing?
publishArtifact in (Compile, packageBin) := false
publishArtifact in run := true
val myZipTask = taskKey[File]("swagger-zip")
myZipTask := {
file("swagger.zip")
}
addArtifact(Artifact("swagger", "zip", "zip"), myZipTask )
The run command doesn't trigger any publishing tasks, as that's not what it's for. I think you have tried to turn on publishing when run is called with "publishArtifact in run := true", but that code is not sufficient to achieve that; you'd need to attach many other Tasks to that command. That's not what you want anyway.
Your case fits entirely within the expected usage of the addArtifact helper (see the docs, and the code)
As you need to run code to generate artefacts, you should provide that code as a Task argument to the addArtifact helper, i.e.
val myZipTask = taskKey[File]("return the swagger-zip file")
val runZipCodeTask = taskKey[Unit]("run the swagger-zip code")
// See http://www.scala-sbt.org/0.13.2/docs/faq.html, “How can I create a custom run task, in addition to run?”
// and https://stackoverflow.com/questions/23409993/defining-sbt-task-that-invokes-method-from-project-code
fullRunTask(runZipCodeTask, Compile, "ZipGeneratorMainClass")
myZipTask := {
runZipCodeTask.value
file("target/swagger/swagger.zip")
}
addArtifact(Artifact("swagger", "zip", "zip"), myZipTask)
Then run "sbt publish"
Working demo
See https://gist.github.com/RichardBradley/5384a5e0da2427df237f42fe512b30b8 for a working demo.
Note about scopes - consider generating this file in a meta-project, not the main project
See https://stackoverflow.com/a/23416018/8261

SBT: How to change the behavior of a task in the scope of another task which is in the scope of yet another task?

I have defined a custom task like this:
myCustomTask := {
val file = assembly.value
println(file)
}
When I run myCustomTask in sbt, it will trigger assembly, and assembly will trigger test.
Is there a way for me to disable test only when assembly is triggered in myCustomTask? In other words, if I run assembly directly in sbt console, the test should still be triggered.
If I use test in assembly := {}, it will skip all the test no matter how assembly is called, which is not I want. On the other hand, test in myCustomTask := {} seems to have no effect at all.

How to run sbt tests for debugging when debug is disabled by default?

I find it incredibly awkward to have to restart sbt with special flags if I want to run the tests (or a main) with debug enabled. It's also a pain if the main or test is usually in a forked JVM:
How to set fork in Test when -jvm-debug given on command line?
Is there any simple way to conditionally do a run, test, test-quick or test-only and ask for debugging to be enabled in the forked process? e.g. with syntax like test-only -jdb
I don't really want to have to write my own Tasks to do this... maintaining them is going to be a nightmare. But I guess that would allow syntax like module/jdb:test-only
While Eugene accurately mentions that we could provide debug:testOnly out of the box, the following should help you out:
val DebugTest = config("dtest") extend Test
lazy val myproject =
project.configs(DebugTest).
settings(inConfig(DebugTest)(Defaults.testSettings):_*).
settings(
fork in DebugTest := true,
javaOptions in DebugTest += "debugging options",
definedTests in DebugTest := (definedTests in Test).value
)
This should allow you to do dtest:testOnly *, dtest:run and dtest:test on myproject. The forked JVM (note fork in DebugTest := true) will use whatever debugging options you've provided.
I don't think there's any simple feature that enables debugging out of the box.
As you mentioned on sbt-dev list, making a custom configuration like debug:testOnly sounds like a good strategy.

sbt subproject aggregation and dependency behavior

I have an sbt project with a few subprojects, each of which publishes some artifacts and has a fairly extensive test suite.
When I run the build on my CI server, I want to publish the artifacts to a staging location and run the tests after the publishing task. Since others may want the artifacts, I'd like to tell sbt that I want it to build all the artifacts for all subprojects, then run all the tests, since by default it seems to run them interleaved in an unspecified order.
I have a ScopeFilter giving me access to all my subprojects, so I can make my ciBuild task depend on something like the following
(test in Test).all(subprojectScopeFilter).dependsOn(myArtifactsTask.all(subprojectScopeFilter))`
However, that doesn't seem to have any real effect on the order, and I definitely see some subprojects running tests before others have run their myArtifactsTask. I'm guessing that I don't fully understand how all works and it might be saying that each independent subproject's test task depends on that same subproject's myArtifactsTask? If that's the case, how can I specify what I want? Is it documented somewhere that I've missed? The manual describes the basics of all but not how it interacts with other constructs.
SBT will resolve automatically the order between task and projects and build them in that order.
What you could do is - let's assume you have three projects. Root and two sub-projects. I assume that the key myArtifactTask is defined in the root.
project/Build.scala
object MyBuild extends Build {
val myArtifactTask = TaskKey[Unit]("my-artifact-task", "My Artifact Task")
}
The myArtifactTask is implemented in both sub-projects.
subproject-a/build.sbt
myArtifactTask := {
println("myArtifactTask:project-a")
}
subproject-a/build.sbt
myArtifactTask := {
println("myArtifactTask:project-b")
}
What you want to do is to define your root's build.sbt in a way that it calls myArtifactTask in both projects. Then you could define new task testedArtifact which would depend on myArtifactTask.
build.sbt
lazy val testedArtifact = taskKey[Unit]("Runs myArtifactTask followed by tests")
lazy val inAnyProjectButRoot: ScopeFilter = ScopeFilter (
inAnyProject -- inProjects(ThisProject)
)
myArtifactTask := {
myArtifactTask.all(inAnyProjectButRoot).value
}
testedArtifact := {
(test in Test).all(anyProjectButRoot).value
}
testedArtifact <<= testedArtifact.dependsOn(myArtifactTask)
Now calling testedArtifactin the root project will first call all myArtifactTasks in sub-projects followed by tests.