How to make sbt run another consecutive command regardless of previous command's result? - scala

My tests are slow. Real slow. Like I can get another cup of coffee and reading some articles while waiting for them to finished slow. So I added this task to build.sbt just to alert me when my testing is finished.
lazy val alertMe = taskKey[Unit]("Alert me when testing is completed.")
alertMe in Test := {
"say \"testing is completed\""!
}
Noted that I use say command on OS X. I then used this task like this.
;test ;alertMe
Voila! This works great.... only for successful testing. In case that any test case failed, test task return result as error, and alertMe is not invoked.
This behavior is pretty understandable. but I want my task, alert me, to run regardless of test task result. How can I do this ?

Maybe you can add test task in alertMe task, like:
lazy val alertMe = taskKey[Unit]("Alert me when testing is completed.")
alertMe := {
Command.process("test", state.value)
"say \"testing is completed\""!
}
usage: sbt alertme, it will run the test task and the shell command.
Command.process will execute the test task and without causing current task fail. so the commands always will be executed.

Related

Is there a simple way to fork a server process in sbt before starting test against that server?

my e2e test task sends some http requests to the server. i want to start that server (Play framework based) on a separate jvm, then start the test which hits the server and let it finish, then stop the server.
i looked through many SO threads so far found these options:
use sbt-sequential
use sbt-revolver
use alias
but in my experiments setting fork doesn't work, i.e. it still blocks execution when server is started
fork := true
fork in run := true
fork in Test := true
fork in IntegrationTest := true
The startServer/stopServer examples in sbt docs are also blocking it seems
I also tried just starting the server in background from shell but server is quickly shut down, similar to this question
nohup sbt -Djline.terminal=jline.UnsupportedTerminal web/run < /dev/null > /tmp/sbt.log 2>&1 &
related questions:
scala sbt test run setup and cleanup command once on multi project
How do I start a server before running a test suite in SBT?
fork doesn't run task in parallel - it just makes sure that tests are run in a separate JVM which helps with things like shutdown webhooks or disconnecting from services that doesn't handle resource release properly (e.g. DB connection that never calls disconnect).
If you want to use the same sbt to start server AND run test against that instance (which sounds like easily breakable antipattern BTW) you can use somethings like:
reStart
it:test
reStop
However that would be tricky because reStart yields immediately so tests would start when the server setup started but not necessarily completed. Race condition, failing tests, or blocking all tests until server finishes starting.
This is why nobody does it. Much easier to handle solution is to:
start the server in test in some beforeAll method and make this method complete only after server is responding to queries
shutdown it in some afterAll method (or somehow handle both of these using something like cats.effect.Resource or similar)
depending on situation:
running tests sequentially to avoid starting two instances at the same time or
generating config for each test so that they could be run in parallel without clashing on ports allocations
Anything else is just a hack that is going to fail sooner rather than later.
answering my own question, what we ended up doing is
use "sbt stage" to create standalone server jar & run script for the Play web app (in web/target/universal/stage/bin/)
create run_integration_tests.sh shell script that starts the server, waits 30 sec and starts test
add runIntegrationTests task in build.sbt which calls run_integration_tests.sh, and add it to it:test
run_integration_tests.sh:
#! /bin/bash
CURDIR=$(pwd)
echo "Starting integration/e2e test runner"
date >runner.log
export JAVA_OPTS="-Dplay.server.http.port=9195 -Dconfig.file=$CURDIR/web/conf/application_test.conf -Xmx2G"
rm -f "$CURDIR/web/target/universal/stage/RUNNING_PID"
echo "Starting server"
nohup web/target/universal/stage/bin/myapp >>runner.log 2>&1 &
echo "Webserver PID is $pid"
echo "Waiting for server start"
sleep 30
echo "Running the tests"
sbt "service/test:run-main com.blah.myapp.E2ETest"
ERR="$?"
echo "Tests Done at $(date), killing server"
kill $pid
echo "Waiting for server exit"
wait $pid
echo "All done"
if [ $ERR -ne 0 ]; then
cat runner.log
exit "$ERR"
fi
build.sbt:
lazy val runIntegrationTests = taskKey[Unit]("Run integration tests")
runIntegrationTests := {
val s: TaskStreams = streams.value
s.log.info("Running integration tests...")
val shell: Seq[String] = Seq("bash", "-c")
val runTests: Seq[String] = shell :+ "./run_integration_tests.sh"
if ((runTests !) == 0) {
s.log.success("Integration tests successful!")
} else {
s.log.error("Integration tests failed!")
throw new IllegalStateException("Integration tests failed!")
}
}
lazy val root = project.in(file("."))
.aggregate(service, web, tools)
.configs(IntegrationTest)
.settings(Defaults.itSettings)
.settings(
publishLocal := {},
publish := {},
(test in IntegrationTest) := (runIntegrationTests dependsOn (test in IntegrationTest)).value
)
calling sbt in CI/jenkins:
sh 'sbt clean coverage test stage it:test'

Run a task after run

I start some docker containers before executing run to start my play-framework project:
run in Compile := (run in Compile dependsOn(dockerComposeUp)).evaluated
Now I'd like to tear down all docker containers using dockerComposeDown when play stops. Any ideas on how to accomplish on this?
I've already gone through Doing something after an input task, but that starts the containers and immediatly stops them again. (In fact it even stops the containers before starting them.) Here is what I tried:
run in Compile := {
(run in Compile dependsOn(dockerComposeUp)).evaluated
dockerComposeDown.value
}
A different approach is to call your docker task sequentially to run task. You could achieve this as described below:
lazy val testPrint = taskKey[Unit]("showTime")
testPrint := {
println("Test print.")
}
lazy val testRun = taskKey[Unit]("test build")
testRun := {
Def.sequential((runMain in Compile).toTask(" com.mycompany.MainClass "), testPrint).value
}
First define the testPrint task which in your case could be the dockerTask and then define testRun which will run both tasks sequentially. To run this just do sbt testRun. After execution it should print out "Test print."

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.

prompt for user input when running scala program with sbt

I have a very simple scala program:
object TakeInputs {
def main(args: Array[String]) {
val name = readLine("What is your name?")
println(name)
}
}
When I try to run this with
sbt "project myproject" "run-main TakeInput"
it doesn't wait for user input and the program just finishes with
What is your name?null
as the output.
Is there a way to make sbt wait for user input (like what happens if "readLine" is run in sbt console)? I can provide the inputs as command line parameters but I have a lot of them and I would like to make the program more user-friendly by displaying messages indicating what the user should enter next. Thanks.
Add the following to your build.sbt
connectInput in run := true
From the sbt documentation in Configuring Input

gradle get value from extension (pass as input to task)

I have following code in my plugin:
#Override
void apply(Project project) {
project.extensions.create(EXTENSION,TestExtension)
project.task("task1") << {
println "Task 1"
println(project.mmm.test)
def extension = project.extensions.findByName(EXTENSION)
println(extension.test)
}
project.task("task2",type: TestTask) {
println "Task 2 "
def extension = project.extensions.findByName(EXTENSION)
// conventionMapping.test = {extension.test}
// println(project.extensions.findByName(EXTENSION).test)
// test = "test"
}
}
In task 1 extension.test return correct value. However in task2 extension.test always return null. What I am doing wrong? Is there a better way to pass some of the extensions values as input for task? I am using gradle 1.12 with jdk 1.8 on Mac. Best Regards
Edit :correct version:
project.task("task2", type: TestTask) {
project.afterEvaluate {
def extension = project.extensions.findByName(EXTENSION)
println(project.extensions.findByName(EXTENSION).test)
test = project.extensions.findByName(EXTENSION).test
}
}
task1 prints the value at execution time (notice the <<), task2 at configuration time (before the rest of the build script after the apply plugin: ... has been evaluated). This explains why the println for task1 works as expected, and the println for task2 doesn't.
However, configuring a task at execution time is too late. Instead, a plugin needs to defer reading user-provided values until the end of the configuration phase (after build scripts have been evaluated, but before any task has been executed). There are several techniques for doing so. One of the simpler ones is to wrap any such read access with project.afterEvaluate { ... }.
Updated answer as some time has passed and Gradle evolved its concepts and its syntax in the meantime.
To use up-to-date and optimally configured task you should use following syntax:
tasks.register("task1") {
doLast {
def extension = project.extensions.findByName(EXTENSION)
println(project.extensions.findByName(EXTENSION).test)
test = project.extensions.findByName(EXTENSION).test
}
}
Task Action:
A task has both configuration and actions. When using the doLast, you
are simply using a shortcut to define an action. Code defined in the
configuration section of your task will get executed during the
configuration phase of the build regardless of what task was targeted.
Deprecated << Operator
<< was deprecated in 4.x and removed in 5.0. See task action (doLast) on how to evaluate task logic at execution phase when all extensions and configurations should be evaluated.
Task Configuration Avoidance
To avoid the cost of creating a task if this won't be executed on your invoked Gradle command TaskContainer.register(String) method.
Avoid afterEvaluate
afterEvaluate should in most cases be avoided, see #sterling's comments from the link. You have now the possibility to evaluate task action part in execution phase, and additionally you can also rely on Task Inputs/Outputs with the combination of Lazy Configuration.