Sequential test setup/cleanup in sbt - scala

I have an SBT project with multiple subprojects aggregated within a root project. I want to make sure that test suites are ran sequentially, so I have set Global / concurrentRestrictions += Tags.limit(Tags.Test, 1).
However, I am also using Tests.Setup()/Cleanup() to initialise memory-heavy resources for each subproject's test suite. The problem I'm facing is that concurrentRestrictions has no effect on test setup and cleanup: all of my test setup and cleanup logic is being ran in parallel, when I would like it to run sequentially before and after each test suite.
Here is a sample build.sbt:
import sbt._
import scala.sys.process._
lazy val testSettings = Seq(
testOptions ++= Seq(
Tests.Setup(() => {
println(s"setup for ${name.value}")
}),
Tests.Cleanup(() => {
println(s"cleanup for ${name.value}")
})
)
)
Global / libraryDependencies ++= Seq(
"org.scalatest" %% "scalatest" % "3.1.0" % "test"
)
def module(path: String) = {
Project(path, file(path))
.settings(testSettings)
}
lazy val module1 = module("module1")
lazy val module2 = module("module2")
lazy val module3 = module("module3")
lazy val root = (project in file("."))
.aggregate(module1, module2, module3)
Global / concurrentRestrictions += Tags.limit(Tags.Test, 1)
and a dummy test I'm using for each subproject:
import org.scalatest.matchers.should.Matchers
import org.scalatest._
class DummyTest extends FlatSpec with Matchers {
"this test" should "run a test for module 1" in {}
}
Running sbt test gives the following output, modulo the vagaries of concurrency:
$ sbt test
[info] Loading settings for project global-plugins from plugins.sbt ...
[info] Loading global plugins from /home/jejost/.sbt/1.0/plugins
[info] Loading project definition from /home/jejost/dev/tmp/sbt-yunowork/project
[info] Loading settings for project root from build.sbt ...
[...]
setup for module1
setup for module3
setup for module2
[info] DummyTest:
[info] this test
[info] - should run a test for module 1
[info] DummyTest:
[info] this test
[info] - should run a test for module 3
[info] DummyTest:
[info] this test
[info] - should run a test for module 2
cleanup for module1
cleanup for module3
cleanup for module2
[...]
when the behaviour I want is this:
$ sbt test
[info] Loading settings for project global-plugins from plugins.sbt ...
[info] Loading global plugins from /home/jejost/.sbt/1.0/plugins
[info] Loading project definition from /home/jejost/dev/tmp/sbt-yunowork/project
[info] Loading settings for project root from build.sbt ...
[...]
setup for module1
[info] DummyTest:
[info] this test
[info] - should run a test for module 1
cleanup for module1
setup for module3
[info] DummyTest:
[info] this test
[info] - should run a test for module 3
cleanup for module3
setup for module2
[info] DummyTest:
[info] this test
[info] - should run a test for module 2
cleanup for module2
[...]
(Ordering of module1, module3, module2 doesn't matter to me as long as setup/teardown is sequential before/after each subproject)
Is it possible to ensure sequential test setup and cleanup like this, ideally without defining my own custom task?
I am using SBT 1.3.4.
EDIT: this has been flagged as a duplicate of the following question: How to run subprojects tests (including setup methods) sequentially when testing
This is indeed the same problem, however, the accepted answer does not work and does not change anything to the task run order! I tried to reproduce the other question's setup to see if it's a regression in SBT, unfortunately the OP's SBT version is too old to build successfully on my machine.

Commands, unlike tasks, are by default executed sequentially so consider simply dropping down to command level
addCommandAlias("sequentialtest", ";module1/test;module2/test;module3/test;")
which gives something like
sbt:hello-world-scala> sequentialtest
setup for module1
[info] DummyTest:
[info] this test
[info] - should run a test for module 1
cleanup for module1
[info] Run completed in 164 milliseconds.
...
setup for module2
[info] DummyTest:
[info] this test
[info] - should run a test for module 2
cleanup for module2
[info] Run completed in 148 milliseconds.
...
setup for module3
[info] DummyTest:
[info] this test
[info] - should run a test for module 3
cleanup for module3
[info] Run completed in 154 milliseconds.
...
Addressing the comment
commands += Command.command("sequentialtest") { state =>
Project
.extract(state)
.structure
.allProjects
.iterator
.map(_.id)
.filterNot(_ == "root")
.map(id => s"$id/test;")
.mkString :: state
}

Related

Why can't SBT run a specific main?

My Scala 3 code starts with
#main def primer(): Unit = {
. . .
}
and when I use
% sbt run
It runs fine...
% sbt run
[info] welcome to sbt 1.6.2 (Oracle Corporation Java 11.0.13)
[info] loading global plugins from /Users/eric.kolotyluk/.sbt/1.0/plugins
[info] loading project definition from /Users/eric.kolotyluk/git/autonomous-iam/poc/fp-laboratory/project
[info] loading settings for project root from build.sbt ...
[info] set current project to fp-laboratory (in build file:/Users/eric.kolotyluk/git/autonomous-iam/poc/fp-laboratory/)
[info] running com.forgerock.fp.laboratory.primer
Primer running... with 11.0.13+10-LTS-370
Note: SBT believes it is running com.forgerock.fp.laboratory.primer
But, if I try
% sbt "runMain com.forgerock.fp.laboratory.primer"
[info] welcome to sbt 1.6.2 (Oracle Corporation Java 11.0.13)
[info] loading global plugins from /Users/eric.kolotyluk/.sbt/1.0/plugins
[info] loading project definition from /Users/eric.kolotyluk/git/autonomous-iam/poc/fp-laboratory/project
[info] loading settings for project root from build.sbt ...
[info] set current project to fp-laboratory (in build file:/Users/eric.kolotyluk/git/autonomous-iam/poc/fp-laboratory/)
[info] running com.forgerock.fp.laboratory.primer
Primer running... with 11.0.13+10-LTS-370
Exception in thread "sbt-bg-threads-1" java.lang.NoClassDefFoundError: org/apache/spark/sql/SparkSession
at com.forgerock.fp.laboratory.Primer$package$.primer(Primer.scala:30)
So, clearly, run and runMain do not do the same thing.
Is there a way to make runMain do the right thing?
Is this caused by some weirdness with the Scala 3 #main annotation?

Why does sbt 1.0.2 hang after compilation?

I have installed sbt 1.0.2 and tried building the "hello project", but it hangs after done compilation.
I waited for 2 hours and it does not complete. what am I missing here?
HOSTPC:IdeaProjects XYZ2017$ sbt new sbt/scala-seed.g8
[info] Loading settings from plugins.sbt ...
[info] Loading global plugins from /Users/XYZ2017/.sbt/1.0/plugins
[info] Set current project to ideaprojects (in build file:/Users/XYZ2017/IdeaProjects/)
A minimal Scala project.
name [Scala Seed Project]: hello
Template applied in ./hello
HOSTPC:IdeaProjects XYZ2017$ ls
hello
HOSTPC:IdeaProjects XYZ2017$ cd hello/
HOSTPC:hello XYZ2017$ sbt
[info] Loading settings from plugins.sbt ...
[info] Loading global plugins from /Users/XYZ2017/.sbt/1.0/plugins
[info] Loading project definition from /Users/XYZ2017/IdeaProjects/hello/project
[info] Updating {file:/Users/XYZ2017/IdeaProjects/hello/project/}hello-build...
[info] Done updating.
[info] Compiling 1 Scala source to /Users/XYZ2017/IdeaProjects/hello/project/target/scala-2.12/sbt-1.0/classes ...
[info] Done compiling.

SBT aggregating subproject

Given a project structure:
rootProject
aggregatingSubProject
subA
subB
subC
How do I run the package task for aggregatingSubProject and all its aggregated projects, without specifying them by hand?
So far I can only have subA and subB built when doing package in the rootProject - regardless if I put .aggregate(subA, subB) in aggregatingProject's build.sbt.
I need the rootProject to define common settings for the build and I want to build multiple projects (some aggregating other projects, like aggregatingSubProject does) in a single build.
EDIT: I need to do this without specifying all the sub-sub-projects in the root build.sbt. I'd like to define subA and subB in aggregatingSubProject/build.sbt.
Using sbt 0.13.16.
Given this in build.sbt:
val rootProject = project in file(".")
val aggregatingSubProject = project
val subC = project
and this in aggregatingSubProject/build.sbt:
aggregateProjects(subA, subB)
val subA = project
val subB = project
You can run aggregatingSubProject/package and get:
> aggregatingSubProject/package
[info] Updating {file:/s/t-subaggregate/}subB...
[info] Updating {file:/s/t-subaggregate/}aggregatingSubProject...
[info] Updating {file:/s/t-subaggregate/}subA...
[info] Done updating.
[info] Packaging /s/t-subaggregate/aggregatingSubProject/subA/target/scala-2.12/suba_2.12-0.1-SNAPSHOT.jar ...
[info] Done updating.
[info] Done packaging.
[info] Packaging /s/t-subaggregate/aggregatingSubProject/subB/target/scala-2.12/subb_2.12-0.1-SNAPSHOT.jar ...
[info] Done packaging.
[info] Done updating.
[info] Packaging /s/t-subaggregate/aggregatingSubProject/target/scala-2.12/aggregatingsubproject_2.12-0.1-SNAPSHOT.jar ...
[info] Done packaging.

Specs2 unable to find my tests using sbt

I have build a simple project for Spec2 testing using simple sbt.
package main.specs
import org.specs2._
class QuickStartSpec extends Specification {
def is = s2"""
This is my first specification
it is working $ok
really working! $ok
"""
}
And here is my build.sbt file:
name := "QuickStartSpec"
version := "1.0"
scalaVersion := "2.10.1"
libraryDependencies ++= Seq("org.specs2" %% "specs2-core" % "3.6.5" % "test")
scalacOptions in Test ++= Seq("-Yrangepos")
But when I run this command in sbt
testOnly main.specs.QuickStartSpec
I am getting this:
[info] Updating {file:/Users/nabajeet/workspace/SpecsTest/}specstest...
[info] Resolving org.fusesource.jansi#jansi;1.4 ...
[info] Done updating.
[info] Passed: Total 0, Failed 0, Errors 0, Passed 0
[info] No tests to run for test:testOnly
I am following this page to create the example:
https://etorreborre.github.io/specs2/website/SPECS2-3.6.5/quickstart.html
I am unable to figure out the reason why my tests are not detected.
My sbt version in 0.13.8
By declaring
libraryDependencies ++= Seq("org.specs2" %% "specs2-core" % "3.6.5" % "test")
You restrict the scope of specs2 to only classes in the test source directories. You won't be able to reference specs2 classes in the production code (all the code under src/main/)
In your comment you indicate that you placed your spec in /Users/nabajeet/workspace/SpecsTest/src/main/specs/quickStartSpec.scala
Try moving your file to /Users/nabajeet/workspace/SpecsTest/src/test/scala/specs/quickStartSpec.scala
The incorrect location is why it you spec not picked up by SBT (and I feel confident to say that it doesn't compile either).
By default, SBT applies maven's standard directory layout adding src/main/scala/ and src/test/scala/ for scala code. This is documented in the SBT tutorial
I just created a project with the following layout
.
./built.sbt
./src
./src/test
./src/test/scala
./src/test/scala/QuickStartSpec.scala
build.sbt contains
name := "QuickStartSpec"
version := "1.0"
scalaVersion := "2.11.4"
libraryDependencies ++= Seq("org.specs2" %% "specs2-core" % "3.6.5" % "test")
scalacOptions in Test ++= Seq("-Yrangepos")
and QuickStartSpec.scala contains
package main.specs
import org.specs2._
class QuickStartSpec extends Specification {
def is = s2"""
This is my first specification
it is working $ok
really working! $ok
"""
}
here is the sbt output I get
sbt
[info] Set current project to QuickStartSpec (in build file:/tmp/stack/)
> test:compile
[info] Updating {file:/tmp/stack/}stack...
[info] Resolving jline#jline;2.12 ...
[info] Done updating.
[info] Compiling 1 Scala source to /tmp/stack/target/scala-2.11/test-classes...
[info] 'compiler-interface' not yet compiled for Scala 2.11.4. Compiling...
[info] Compilation completed in 6.372 s
[success] Total time: 9 s, completed 27 nov. 2015 06:38:26
> test
[info] QuickStartSpec
[info] + This is my first specification
[info] it is working
[info] + really working!
[info]
[info] Total for specification QuickStartSpec
[info] Finished in 17 ms
[info] 2 examples, 0 failure, 0 error
[info]
[info] Passed: Total 2, Failed 0, Errors 0, Passed 2
[success] Total time: 1 s, completed 27 nov. 2015 06:38:31
>

Using ScalaTest with SBT android-sdk-plugin

I'm trying to use SBT with android-sdk-plugin and ScalaTest, but have no success.
I could run test command in SBT console, but it didn't find any ScalaTest test suite in my src/test/scala folder.
I got the following output from SBT, which seems didn't run any ScalaTest test suite at all.
[info] Compiling 2 Scala sources and 3 Java sources to /home/brianhsu/AndroidProject/FindLost/target/android-bin/classes...
[info] Packaging /home/brianhsu/AndroidProject/FindLost/target/android-bin/classes.jar ...
[info] Done packaging.
[info] Packaging /home/brianhsu/AndroidProject/FindLost/target/scala-2.10/findlost_2.10-0.1-SNAPSHOT-tests.jar ...
[info] Done packaging.
[info] Run completed in 44 milliseconds.
[info] Total number of tests run: 0
[info] Suites: completed 0, aborted 0
[info] Tests: succeeded 0, failed 0, canceled 0, ignored 0, pending 0
[info] No tests were executed.
[info] No tests to run for test:test
The following is how I create the project:
Using android create project -g to create an Android project with gradle layout.
Add addSbtPlugin("com.hanhuy.sbt" % "android-sdk-plugin" % "1.2.11") to project/plugins.sbt to include android-sdk-plugin to my project.
Add ScalaTest to my libraryDependencies setting in build.sbt, which makes the build file looks like the following:
import android.Keys._
android.Plugin.androidBuild
name := "FindLost"
scalaVersion := "2.10.4"
organization := "org.bone.findlost"
libraryDependencies += "org.scalatest" % "scalatest_2.10" % "2.1.0" % "test"
platformTarget in Android := "android-19"
run <<= run in Android
install <<= install in Android
Add a ScalaTest test case to src/test/scala, which contains example test suite from QuickStart page of ScalaTest.
Place your tests in src/androidTest/scala
The android-sdk-plugin uses androidTest for both Android instrumentation tests as well as regular tests.
See Ordinary scalatests not found from src/test #45 for a discussion on this topic. Note, that since that discussion the plugin changed directory has changed to the path I listed.