Sequential run of tests in SBT: Are these configurations the same? - scala

Are these 2 settings alternatives or are both necessary to run the tests sequentially ?
Global / concurrentRestrictions += Tags.limit(Tags.Test, 1)
And :
parallelExecution := false
Another question :
Does the first configuration guarantee to have only one task per all the tests whatever it is their Test Suite?

Related

How to make SBT run test suites in parallel?

I have a bunch of integration tests running by sbt, given test N suites with 1..M tests per each suite.
I have set fork in IntegrationTest := true, but test suites are always executed sequentially. According to the docs, this must not be the case: test suites should be executed concurrently.
the test suites are a class as following:
class MyTestSuite1 extends FlatSpec with Matchers
...
it should "do A" {}
it should "do B" {}
class MyTestSuite2 extends FlatSpec with Matchers
...
it should "do C" {}
it should "do D" {}
the problem
MyTestSuite1 and MyTestSuiteN are executed sequentially (by the alphabet order to be exact)
expectation
MyTestSuite1 and MyTestSuiteM are executed concurrently
env
.sbopts:
-J-Xms1G
-J-Xmx4G
-J-XX:MaxMetaspaceSize=512m
-J-Xss4M
note
I noticed that all test are running using the same pool and thread, for example, pool-1-thread-1 for all tests.
sbt version: 1.2.8
Scala: 2.12.8
os: MacOS 10.15, Ubuntu 19.04
Scalatest ver: 3.2.0-SNAP10
Tried sbt v. 1.3.2 - same result.
Adding
testOptions in IntegrationTest += Tests.Argument(TestFrameworks.ScalaTest, "-P4"),
does not help.
============
Update
fork in(IntegrationTest, test) := true works on a global level, but I have 2 projects and I want to make it work to preserve relative path to the proj.
e.g.
lazy val `p1` = Project(id = "p1", base = file("./p1"))
.configs(IntegrationTest)
.settings(Defaults.itSettings: _*)
.settings(
fork in(IntegrationTest, test) := true,
...)
lazy val `p2` = Project(id = "p2", base = file("./p2"))
.configs(IntegrationTest)
.settings(Defaults.itSettings: _*)
.settings(
fork in(IntegrationTest, test) := true,
...)
does not run tests in parallel
instead, this runs in parallel, but, obviously, the home dir is set to be "." rather than to be "./p1" or "./p2" respectively:
fork in(IntegrationTest, test) := true
lazy val `p1` = Project(id = "p1", base = file("./p1"))
.configs(IntegrationTest)
.settings(Defaults.itSettings: _*)
SOLUTION:
It appears there's testForkedParallel in IntegrationTest := true option which does exactly what I needed - it spawns new JVM per test suite.
==============
Remark:
So, the only problem is that now it spawns JVMs as many as the count of all available CPUs and I can't funnel only test concurrency:
OPTION 1 - funnels all sbt processes to be only 4 in parallel
concurrentRestrictions in Global := Seq(Tags.limitAll(4))
OPTION 2 - just does nothing (test are in the subproject)
concurrentRestrictions in Global += Tags.limit(Tags.Test, 4),
By default tests executed in forked JVM are executed sequentially. Refer the following para from sbt testing docs:
The setting:
Test / fork := true
specifies that all tests will be executed in a single external JVM.
See Forking for configuring standard options for forking. By default,
tests executed in a forked JVM are executed sequentially. More control
over how tests are assigned to JVMs and what options to pass to those
is available with testGrouping key.
So, you have two options:
Do not fork JVM and your tests will run parallelly by default
If you want to fork and still rut tests parallelly go through this docs:
https://www.scala-sbt.org/1.x/docs/Testing.html#Forking+tests

How to run Test Suites sequentially in ScalaTest / SBT?

How to run Test Suites sequentially in ScalaTest / SBT?
For example if I have this test suites A, B and C I want to make sure that the tests of A will be run 1st then the ones of B then the ones of C.
Is there in configuration that I can make in Scalatest or SBT?
Thank you.
try using parallelExecution in Test := false
According to the documentation http://doc.scalatest.org/1.7/org/scalatest/Suite.html
You need to create your own Test Suite like the following:
FirstTest.scala
import org.scalatest.{DoNotDiscover, FunSuite}
#DoNotDiscover
class FirstTest extends FunSuite {
test("first test"){
assert(1 == 1)
}
}
SecondTest.scala
import org.scalatest.{DoNotDiscover, FunSuite}
#DoNotDiscover
class SecondTest extends FunSuite{
test("second test"){
assert(2 == 2)
}
}
MainTest.scala
import org.scalatest.Suites
class MainTest extends Suites (new FirstTest,new SecondTest)
Now, if you run sbt test it's work properly.
Notes: the property #DoNotDiscover is mandatory. this avoid unexpected behavior like execution of FirstTest and SecondTest after the execution of the MainSuite that are already executed the two test suites.
I hope it was helpful
As raj mehra said, the solution is to configure to run the tests not in parallel.
Test / parallelExecution := false
Former parallelExecution in Test := false is deprecated.
Here is the Documentation that explains it: SBT Parallel Execution
From it:
As before, parallelExecution in Test controls whether tests are mapped to separate tasks.

Multiple SBT Configurations should be exclusive, but they all activate at the same time - why?

I have defined a minimal build.sbt with two custom profiles ‘dev’ and ‘staging’ (what SBT seems to call Configurations). However, when I run SBT with the Configuration that was defined first in the file (dev), both Configuration blocks are executed - and if both modify the same setting, the last one wins (staging).
This seems to break any notion of conditional activation, so what am I doing wrong with SBT?
For reference, I want to emulate the conditionally activated Profiles concept of Maven e.g. mvn test -P staging.
SBT version: 1.2.1
build.sbt:
name := "example-project"
scalaVersion := "2.12.6"
...
fork := true
// Environment-independent JVM property (always works)
javaOptions += "-Da=b"
// Environment-specific JVM property (doesn’t work)
lazy val Dev = config("dev") extend Test
lazy val Staging = config("staging") extend Test
val root = (project in file("."))
.configs(Dev, Staging)
.settings(inConfig(Dev)(Seq(javaOptions in Test += "-Dfoo=bar")))
.settings(inConfig(Staging)(Seq(javaOptions in Test += "-Dfoo=qux")))
Command:
# Bad
sbt test
=> foo=qux
a=b
# Bad
sbt clean dev:test
=> foo=qux
a=b
# Good
sbt clean staging:test
=> foo=qux
a=b
Notice that despite of the inConfig usage you're still setting javaOptions in Test, i.e. in the Test config. If you remove in Test, it works as expected:
...
.settings(inConfig(Dev)(javaOptions += "-Dfoo=bar"))
.settings(inConfig(Staging)(javaOptions += "-Dfoo=qux"))
(also Seq(...) wrapping is unnecessary)
Now in sbt:
> show Test/javaOptions
[info] *
> show Dev/javaOptions
[info] * -Dfoo=bar
> show Staging/javaOptions
[info] * -Dfoo=qux
You can achieve the same result by scoping each setting explicitly (without inConfig wrapping):
.settings(
Dev/javaOptions += "-Dfoo=bar",
Staging/javaOptions += "-Dfoo=qux",
...
)
(here Conf/javaOptions is the same as javaOptions in Conf)

How to disable "Slow" tagged Scalatests by default, allow execution with option?

I want to disable certain automated tests tagged as "Slow" by default but allow the user to enable their execution with a simple command line. I imagine this is a very common use case.
Given this test suite:
import org.scalatest.FunSuite
import org.scalatest.tagobjects.Slow
class DemoTestSuite extends FunSuite {
test("demo test tagged as slow", Slow) {
assert(1 + 1 === 2)
}
test("demo untagged test") {
assert(1 + 1 === 2)
}
}
By default, sbt test will run both tagged and untagged tests.
If I add the following to my build.sbt:
testOptions in Test += Tests.Argument("-l", "org.scalatest.tags.Slow")
Then I get my desired default behavior where untagged tests run and the Slow tagged test will not run.
However, I can't figure out a command line option that will run the slow tests when I want to run them. I've done several searches and tried several examples. I'm somewhat surprised as this seems like a very common scenario.
I had a similar issue: I wanted to have tests that are disabled by default, but run in the release process. I solved it by creating a custom test configuration and setting testOptions in different scopes. So adapting this solution to your case, it should be something along these lines (in your build.sbt):
lazy val Slow = config("slow").extend(Test)
configs(Slow)
inConfig(Slow)(Defaults.testTasks)
Now by default exclude slow tests:
testOptions in Test += Tests.Argument("-l", "org.scalatest.tags.Slow")
But in the Slow scope don't exclude them and run only them:
testOptions in Slow -= Tests.Argument("-l", "org.scalatest.tags.Slow")
testOptions in Slow += Tests.Argument("-n", "org.scalatest.tags.Slow")
Now when you run test in sbt, it will run everything except slow test and when you run slow:test it will run only slow tests.

How to pass scalacOptions (Xelide-below) to sbt via command line

I am trying to call sbt assembly from the command line passing it a scalac compiler flag to elides (elide-below 1).
I have managed to get the flag working in the build.sbt by adding this line to the build.sbt
scalacOptions ++= Seq("-Xelide-below", "1")
And also it's working fine when I start sbt and run the following:
$> sbt
$> set scalacOptions in ThisBuild ++=Seq("-Xelide-below", "0")
But I would like to know how to pass this in when starting sbt, so that my CI jobs can use it while doing different assembly targets (ie. dev/test/prod).
One way to pass the elide level as a command line option is to use system properties
scalacOptions ++= Seq("-Xelide-below", sys.props.getOrElse("elide.below", "0"))
and run sbt -Delide.below=20 assembly. Quick, dirty and easy.
Another more verbose way to accomplish the same thing is to define different commands for producing test/prod artifacts.
lazy val elideLevel = settingKey[Int]("elide code below this level.")
elideLevel in Global := 0
scalacOptions ++= Seq("-Xelide-below", elideLevel.value.toString)
def assemblyCommand(name: String, level: Int) =
Command.command(s"${name}Assembly") { s =>
s"set elideLevel in Global := $level" ::
"assembly" ::
s"set elideLevel in Global := 0" ::
s
}
commands += assemblyCommand("test", 10)
commands += assemblyCommand("prod", 1000)
and you can run sbt testAssembly prodAssembly. This buys you a cleaner command name in combination with the fact that you don't have to exit an active sbt-shell session to call for example testAssembly. My sbt-shell sessions tend to live for a long time so I personally prefer the second option.