Why does scalatest mix up the output? - scala

I run my scalatest from sbt, and the output gets mixed up - scalatest prints all the test run, and comments to them, and somewhere in the middle it prints the statistics:
> test
[info] Compiling 1 Scala source to /home/platon/Tor/scala-dojo-02/target/scala-2.9.1/classes...
[info] FunsWithListsTests:
[info] - should return list of labels
[info] - should return the average rating of games belonging to Zenga
[info] - should return the total ratings of all games
[info] - should return the total ratings of EA games *** FAILED ***
[info] 0 did not equal 170 (FunsWithListsTests.scala:35)
[error] Failed: : Total 8, Failed 5, Errors 0, Passed 3, Skipped 0
[info] - should increase all games rating by 10 *** FAILED ***
[error] Failed tests:
[error] dojo.FunsWithListsTests
[info] List() did not equal List(Game(Activision,40), Game(Zenga,70), Game(Zenga,20), Game(EA,70), Game(EA,120)) (FunsWithListsTests.scala:40)
[info] - should decrease all Zenga games rating by 10 *** FAILED ***
[info] List() did not equal List(Game(Activision,30), Game(Zenga,50), Game(Zenga,0), Game(EA,60), Game(EA,110)) (FunsWithListsTests.scala:45)
[info] - should create function to find Activision games *** FAILED ***
[info] List(Game(Activision,30), Game(Zenga,60), Game(Zenga,10), Game(EA,60), Game(EA,110)) did not equal List(Game(Activision,30)) (FunsWithListsTests.scala:50)
[info] - should return a List of tuples consisting of game label and game *** FAILED ***
[info] List() did not equal List((ACTIVISION,Game(Activision,30)), (ZENGA,Game(Zenga,60)), (ZENGA,Game(Zenga,10)), (EA,Game(EA,60)), (EA,Game(EA,110))) (FunsWithListsTests.scala:56)
[error] {file:/home/platon/Tor/scala-dojo-02/}default-940f03/test:test: Tests unsuccessful
[error] Total time: 1 s, completed Mar 20, 2012 9:27:13 AM
It seems that if I would accumulate a great number of tests, searching for those stats and failed tests would become a pain.
Is there a way to fix this?

It looks to me the reason for that is that SBT by default executes the tests in parallel, the same way maven-surefire-plugin does.
As it is explained in ScalaTest wiki, this can be solved by:
Disable Parallel Execution of Tests
By default, sbt runs all tasks in parallel. Because each test is mapped to a task, tests are also run in parallel by default. To disable parallel execution of tests:
parallelExecution in Test := false

Have the same issue and solved it by saving logs into separate files. Not ideal, but good for CI, especially in case of long-running integration tests. How I did it:
Create a special directory for logs (for example, at the end of build.sbt):
lazy val logDirectory = taskKey[File]("A directory for logs")
logDirectory := {
val r = target.value / "logs"
IO.createDirectory(r)
r
}
[Optionally] Specify a file for a summary information in a project:
testOptions in test += Tests.Argument(
TestFrameworks.ScalaTest, "-fW", (logDirectory.value / "summary.log").toString
)
W disables colors
Create a group for each test. Each group must run a test in a forked JVM with special arguments:
testGrouping in test := testGrouping.value.flatMap { group =>
group.tests.map { suite =>
val fileName = {
// foo.bar.baz.TestSuite -> f.b.b.TestSuite
val parts = suite.name.split('.')
(parts.init.map(_.substring(0, 1)) :+ parts.last).mkString(".")
}
val forkOptions = ForkOptions(
bootJars = Nil,
javaHome = javaHome.value,
connectInput = connectInput.value,
outputStrategy = outputStrategy.value,
runJVMOptions = javaOptions.value ++ Seq(
"-Dour.logging.appender=FILE",
s"-Dour.logging.dir=${logDirectory.value / fileName}"
),
workingDirectory = Some(baseDirectory.value),
envVars = envVars.value
)
group.copy(
name = suite.name,
runPolicy = Tests.SubProcess(forkOptions),
tests = Seq(suite)
)
}
}
Note, we can tell the logback, where we save our logs through our.logging.appender and our.logging.dir
Create a configuration for logging (test/resources/logback-test.xml):
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<target>System.out</target>
<encoder>
<pattern>%date{yyyy-MM-dd HH:mm:ss} %-5level [%thread] %logger{26} - %msg%n</pattern>
</encoder>
</appender>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>${our.logging.dir}/test.log</file>
<append>false</append>
<encoder>
<pattern>%date{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] %logger{26} - %msg%n</pattern>
</encoder>
</appender>
<root level="TRACE">
<appender-ref ref="${our.logging.appender}"/>
</root>
</configuration>
That's all.

Related

display source directories from sbt task for current project as well as source directories of project it dependsOn

sbt sourceDirectories
only displays source directories of current project, but doesn’t display source directories of projects that it depends using
dependsOn(ProjectRef)
Below is the simplified task.
lazy val showAllSourceDirs = taskKey[Unit]("show source directories of all projects")
showAllSourceDirs := {
val projectRefs = loadedBuild.value.allProjectRefs.map(_._1)
projectRefs foreach { projectRef =>
/*
Below line is giving IllegalArgumentException exception :-
[error] java.lang.IllegalArgumentException: Could not find proxy for projectRef: sbt.ProjectRef in
List(value projectRef, value $anonfun, method sbtdef$1, method $sbtdef, object $7fb70afe92bc9a6fedc3,
package <empty>, package <root>) (currentOwner= method $sbtdef )
*/
val sources = (projectRef / Compile / sourceDirectories).value
sources.foreach( println )
}
}
Link for simplified project to reproduce problem :-
https://github.com/moglideveloper/Example
Steps :

Go to ApiSpec directory from command line and run below command :-
sbt showAllSourceDirs
Expected output : print all source directories of Api and ApiSpec project
Actual output : throws java.lang.IllegalArgumentException
I believe you cannot do that, because sbt works with macros. What you can do here, is adding sourceDirectories of Api into sourceDirectories of ApiSpec. This means, that if you add the following into your sbt:
Compile / sourceDirectories ++= (apiModule / Compile / sourceDirectories).value
Then, when running:
sbt sourceDirectories
You get the output:
[info] * /workspace/games/Example/ApiSpec/src/main/scala-2.12
[info] * /workspace/games/Example/ApiSpec/src/main/scala
[info] * /workspace/games/Example/ApiSpec/src/main/java
[info] * /workspace/games/Example/ApiSpec/target/scala-2.12/src_managed/main
[info] * /workspace/games/Example/Api/src/main/scala-2.12
[info] * /workspace/games/Example/Api/src/main/scala
[info] * /workspace/games/Example/Api/src/main/java
[info] * /workspace/games/Example/Api/target/scala-2.12/src_managed/main
There is one thing you need to notice - you are overriding the current sourceDirectories, so be sure you are not using it anywhere else.
Another note, is that you need to add this line on every dependency you have. So I am not sure how big is your project, and how feasible that is.
If you'd like to have a different task, you can do that, bur use the modules themselves, and not thru reflection, again due to macros.
lazy val showAllSourceDirs = taskKey[Unit]("show source directories of all projects")
showAllSourceDirs := {
println((apiSpecProject / Compile / sourceDirectories).value)
println((apiModule / Compile / sourceDirectories).value)
}

kie-maven-plugin fails to generate model with a mix of Java and DRL facts

I'm trying to generate a fully compiled kmodule with kie-maven-plugin. It works when all facts are defined in DRL, but fails when some facts are defined as Java POJOs.
In the same project:
Java fact (src\main\java\org\example\FactA.java):
package org.example;
public class FactA {
private String propA;
public FactA() { }
public FactA(String propA) {
this.propA = propA;
}
public String getPropA() {
return propA;
}
public void setPropA(String propA) {
this.propA = propA;
}
}
DRL Fact (src\main\resources\org\example\decB.drl):
package org.example
import org.example.FactA
declare FactB
factA: FactA
propB : String
end
POM:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<properties>
<generateModel>YES</generateModel>
<runtime.version>7.20.0.Final</runtime.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<groupId>org</groupId>
<artifactId>example</artifactId>
<version>1.0.0</version>
<packaging>kjar</packaging>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-bom</artifactId>
<version>${runtime.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-api</artifactId>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-model-compiler</artifactId>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-canonical-model</artifactId>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-core</artifactId>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-compiler</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.kie</groupId>
<artifactId>kie-maven-plugin</artifactId>
<version>${runtime.version}</version>
<extensions>true</extensions>
</plugin>
</plugins>
</build>
</project>
Output of mvn clean package
[INFO] Scanning for projects...
[INFO]
[INFO] ----------------------------< org:example >-----------------------------
[INFO] Building example 1.0.0
[INFO] --------------------------------[ kjar ]--------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) # example ---
[INFO] Deleting C:\Projects\t\kie-maven-plugin-example\target
[INFO]
[INFO] --- maven-resources-plugin:3.1.0:resources (default-resources) # example ---
[WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 3 resources
[INFO]
[INFO] --- maven-compiler-plugin:3.8.0:compile (default-compile) # example ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding Cp1252, i.e. build is platform dependent!
[INFO] Compiling 1 source file to C:\Projects\t\kie-maven-plugin-example\target\classes
[INFO]
[INFO] --- kie-maven-plugin:7.20.0.Final:generateModel (default-generateModel) # example ---
[INFO] Loading kie.conf from jar:file:/C:/Users/Dmitri/.m2/repository/org/drools/drools-core/7.20.0.Final/drools-core-7.20.0.Final.jar!/META-INF/kie.conf in classloader ClassRealm[extension>org.kie:kie-maven-plugin:7.20.0.Final, parent: sun.misc.Launcher$AppClassLoader#7852e922]
[INFO] Adding Service org.drools.core.io.impl.ResourceFactoryServiceImpl
[INFO] Adding Service org.drools.core.marshalling.impl.MarshallerProviderImpl
[INFO] Adding Service org.drools.core.concurrent.ExecutorProviderImpl
[INFO] Loading kie.conf from jar:file:/C:/Users/Dmitri/.m2/repository/org/drools/drools-compiler/7.20.0.Final/drools-compiler-7.20.0.Final.jar!/META-INF/kie.conf in classloader ClassRealm[extension>org.kie:kie-maven-plugin:7.20.0.Final, parent: sun.misc.Launcher$AppClassLoader#7852e922]
[INFO] Adding Service org.drools.compiler.kie.builder.impl.KieServicesImpl
[INFO] Adding Service org.drools.compiler.builder.impl.KnowledgeBuilderFactoryServiceImpl
[INFO] Loading kie.conf from jar:file:/C:/Users/Dmitri/.m2/repository/org/drools/drools-model-compiler/7.20.0.Final/drools-model-compiler-7.20.0.Final.jar!/META-INF/kie.conf in classloader ClassRealm[extension>org.kie:kie-maven-plugin:7.20.0.Final, parent: sun.misc.Launcher$AppClassLoader#7852e922]
[INFO] Adding Service org.drools.modelcompiler.CanonicalKieModuleProvider
[INFO] Loading kie.conf from jar:file:/C:/Users/Dmitri/.m2/repository/org/kie/kie-internal/7.20.0.Final/kie-internal-7.20.0.Final.jar!/META-INF/kie.conf in classloader ClassRealm[extension>org.kie:kie-maven-plugin:7.20.0.Final, parent: sun.misc.Launcher$AppClassLoader#7852e922]
[INFO] Adding Service org.kie.internal.services.KieAssemblersImpl
[INFO] Adding Service org.kie.internal.services.KieRuntimesImpl
[INFO] Adding Service org.kie.internal.services.KieWeaversImpl
[INFO] Adding Service org.kie.internal.services.KieBeliefsImpl
[INFO] Loading kie.conf from jar:file:/C:/Users/Dmitri/.m2/repository/org/drools/drools-decisiontables/7.20.0.Final/drools-decisiontables-7.20.0.Final.jar!/META-INF/kie.conf in classloader ClassRealm[extension>org.kie:kie-maven-plugin:7.20.0.Final, parent: sun.misc.Launcher$AppClassLoader#7852e922]
[INFO] Adding Service org.drools.decisiontable.DecisionTableProviderImpl
[INFO] Loading kie.conf from jar:file:/C:/Users/Dmitri/.m2/repository/org/drools/drools-scorecards/7.20.0.Final/drools-scorecards-7.20.0.Final.jar!/META-INF/kie.conf in classloader ClassRealm[extension>org.kie:kie-maven-plugin:7.20.0.Final, parent: sun.misc.Launcher$AppClassLoader#7852e922]
[INFO] Adding Service org.drools.scorecards.ScoreCardProviderImpl
[INFO] Loading kie.conf from jar:file:/C:/Users/Dmitri/.m2/repository/org/drools/kie-pmml/7.20.0.Final/kie-pmml-7.20.0.Final.jar!/META-INF/kie.conf in classloader ClassRealm[extension>org.kie:kie-maven-plugin:7.20.0.Final, parent: sun.misc.Launcher$AppClassLoader#7852e922]
[INFO] Adding Service +org.kie.pmml.assembler.PMMLAssemblerService
[INFO] Loading kie.conf from jar:file:/C:/Users/Dmitri/.m2/repository/org/jbpm/jbpm-bpmn2/7.20.0.Final/jbpm-bpmn2-7.20.0.Final.jar!/META-INF/kie.conf in classloader ClassRealm[extension>org.kie:kie-maven-plugin:7.20.0.Final, parent: sun.misc.Launcher$AppClassLoader#7852e922]
[INFO] Adding Service org.jbpm.bpmn2.BPMN2ProcessProviderImpl
[INFO] Adding Service org.jbpm.bpmn2.xml.XmlProcessDumperFactoryServiceImpl
[INFO] Loading kie.conf from jar:file:/C:/Users/Dmitri/.m2/repository/org/jbpm/jbpm-flow-builder/7.20.0.Final/jbpm-flow-builder-7.20.0.Final.jar!/META-INF/kie.conf in classloader ClassRealm[extension>org.kie:kie-maven-plugin:7.20.0.Final, parent: sun.misc.Launcher$AppClassLoader#7852e922]
[INFO] Adding Service org.jbpm.process.builder.ProcessBuilderFactoryServiceImpl
[INFO] Adding Service +org.jbpm.assembler.BPMN2AssemblerService
[INFO] Adding Service +org.jbpm.weaver.BPMN2WeaverService
[INFO] Adding Service +org.jbpm.assembler.DRFAssemblerService
[INFO] Adding Service +org.jbpm.weaver.DRFWeaverService
[INFO] Loading kie.conf from jar:file:/C:/Users/Dmitri/.m2/repository/org/jbpm/jbpm-flow/7.20.0.Final/jbpm-flow-7.20.0.Final.jar!/META-INF/kie.conf in classloader ClassRealm[extension>org.kie:kie-maven-plugin:7.20.0.Final, parent: sun.misc.Launcher$AppClassLoader#7852e922]
[INFO] Adding Service org.jbpm.marshalling.impl.ProcessMarshallerFactoryServiceImpl
[INFO] Adding Service org.jbpm.process.instance.ProcessRuntimeFactoryServiceImpl
[INFO] Loading kie.conf from jar:file:/C:/Users/Dmitri/.m2/repository/org/jbpm/jbpm-case-mgmt-cmmn/7.20.0.Final/jbpm-case-mgmt-cmmn-7.20.0.Final.jar!/META-INF/kie.conf in classloader ClassRealm[extension>org.kie:kie-maven-plugin:7.20.0.Final, parent: sun.misc.Launcher$AppClassLoader#7852e922]
[INFO] Adding Service org.jbpm.casemgmt.cmmn.CMMNCaseProviderImpl
[INFO] Adding Service +org.jbpm.casemgmt.cmmn.assembler.CMMNAssemblerService
[INFO] Loading kie.conf from jar:file:/C:/Users/Dmitri/.m2/repository/org/drools/drools-workbench-models-guided-dtable/7.20.0.Final/drools-workbench-models-guided-dtable-7.20.0.Final.jar!/META-INF/kie.conf in classloader ClassRealm[extension>org.kie:kie-maven-plugin:7.20.0.Final, parent: sun.misc.Launcher$AppClassLoader#7852e922]
[INFO] Adding Service org.drools.workbench.models.guided.dtable.backend.GuidedDecisionTableProviderImpl
[INFO] Loading kie.conf from jar:file:/C:/Users/Dmitri/.m2/repository/org/drools/drools-workbench-models-guided-template/7.20.0.Final/drools-workbench-models-guided-template-7.20.0.Final.jar!/META-INF/kie.conf in classloader ClassRealm[extension>org.kie:kie-maven-plugin:7.20.0.Final, parent: sun.misc.Launcher$AppClassLoader#7852e922]
[INFO] Adding Service org.drools.workbench.models.guided.template.backend.GuidedRuleTemplateProviderImpl
[INFO] Loading kie.conf from jar:file:/C:/Users/Dmitri/.m2/repository/org/drools/drools-workbench-models-guided-scorecard/7.20.0.Final/drools-workbench-models-guided-scorecard-7.20.0.Final.jar!/META-INF/kie.conf in classloader ClassRealm[extension>org.kie:kie-maven-plugin:7.20.0.Final, parent: sun.misc.Launcher$AppClassLoader#7852e922]
[INFO] Adding Service org.drools.workbench.models.guided.scorecard.backend.GuidedScoreCardProviderImpl
[INFO] Loading kie.conf from jar:file:/C:/Users/Dmitri/.m2/repository/org/optaplanner/optaplanner-core/7.20.0.Final/optaplanner-core-7.20.0.Final.jar!/META-INF/kie.conf in classloader ClassRealm[extension>org.kie:kie-maven-plugin:7.20.0.Final, parent: sun.misc.Launcher$AppClassLoader#7852e922]
[INFO] Adding Service +org.optaplanner.core.impl.solver.kie.KieSolverAssemblerService
[INFO] Loading kie.conf from jar:file:/C:/Users/Dmitri/.m2/repository/org/kie/kie-dmn-core/7.20.0.Final/kie-dmn-core-7.20.0.Final.jar!/META-INF/kie.conf in classloader ClassRealm[extension>org.kie:kie-maven-plugin:7.20.0.Final, parent: sun.misc.Launcher$AppClassLoader#7852e922]
[INFO] Adding Service +org.kie.dmn.core.assembler.DMNAssemblerService
[INFO] Adding Service +org.kie.dmn.core.runtime.DMNRuntimeService
[INFO] Adding Service +org.kie.dmn.core.weaver.DMNWeaverService
[INFO] Artifact not fetched from maven: org.drools:drools-bom:${runtime.version}. To enable the KieScanner you need kie-ci on the classpath
[ERROR] package org.example
public class FactB implements org.drools.core.factmodel.GeneratedFact, java.io.Serializable {
public FactB() {
}
public FactB(FactA factA, String propB) {
super();
this.factA = factA;
this.propB = propB;
}
#org.kie.api.definition.type.Position(value = 0)
private FactA factA;
public void setFactA(FactA factA) {
this.factA = factA;
}
public FactA getFactA() {
return factA;
}
#org.kie.api.definition.type.Position(value = 1)
private String propB;
public void setPropB(String propB) {
this.propB = propB;
}
public String getPropB() {
return propB;
}
#Override()
public String toString() {
return "FactB" + "( " + "factA=" + factA + ", " + "propB=" + propB + " )";
}
}
[ERROR] Unable to build KieBaseModel:defaultKieBase
CompilationProblemErrorResult: cannot find symbol
symbol: class FactA
location: package org.example
CompilationProblemErrorResult: cannot find symbol
symbol: class FactA
location: class org.example.FactB
CompilationProblemErrorResult: cannot find symbol
symbol: class FactA
location: class org.example.FactB
CompilationProblemErrorResult: cannot find symbol
symbol: class FactA
location: class org.example.FactB
CompilationProblemErrorResult: cannot find symbol
symbol: class FactA
location: class org.example.FactB
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 6.465 s
[INFO] Finished at: 2019-04-26T09:00:16-04:00
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.kie:kie-maven-plugin:7.20.0.Final:generateModel (default-generateModel) on project example: Execution default-generateModel of goal org.kie:kie-maven-plugin:7.20.0.Final:generateModel failed: Unable to get KieModule, Errors Existed: Error Messages:
[ERROR] Message [id=1, kieBase=defaultKieBase, level=ERROR, path=null, line=5, column=0
[ERROR] text=cannot find symbol
[ERROR] symbol: class FactA
[ERROR] location: package org.example]
[ERROR] Message [id=2, kieBase=defaultKieBase, level=ERROR, path=null, line=12, column=0
[ERROR] text=cannot find symbol
[ERROR] symbol: class FactA
[ERROR] location: class org.example.FactB]
[ERROR] Message [id=3, kieBase=defaultKieBase, level=ERROR, path=null, line=19, column=0
[ERROR] text=cannot find symbol
[ERROR] symbol: class FactA
[ERROR] location: class org.example.FactB]
[ERROR] Message [id=4, kieBase=defaultKieBase, level=ERROR, path=null, line=21, column=0
[ERROR] text=cannot find symbol
[ERROR] symbol: class FactA
[ERROR] location: class org.example.FactB]
[ERROR] Message [id=5, kieBase=defaultKieBase, level=ERROR, path=null, line=25, column=0
[ERROR] text=cannot find symbol
[ERROR] symbol: class FactA
[ERROR] location: class org.example.FactB]
[ERROR] ---
[ERROR] Warning Messages:
[ERROR] ---
[ERROR] Info Messages:
[ERROR]
[ERROR] -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/PluginExecutionException
It's probably a bug in the generation of declared types with the executable model.
Can you kindly post your reproducer here?
https://issues.jboss.org/projects/DROOLS/issues
In the meanwhile, as a workaround, can you try use the fully qualified name while using the class in the declared type?
package org.example
declare FactB
factA: org.example.FactA
propB : String
end
I've tried this against current master and it works.
Thanks for your help
It was a bug fixed here
https://github.com/kiegroup/drools/commit/c36f3ebf1f02e54742c8b4056e7b64bb5deaed07
Thanks for reporting

Is there a scalaTest method or attribute that can be used to run ONLY once just after all tests in all suites

I have a group of test suites that will grow too, and I need to run a testcleanup task that should run only once just after all the tests have finished execution and before the test process exits. This is similar to .NET AssebmlyCleanup, but I couldn't find something equivalent in the Scala/Scalatest world without having to do custom code, is there?
Thanks
I've been thinking how to approach this and one way to do this is to override a bit test and testOnly in our build.sbt.
So suppose we have under src/test/scala following two suites:
class Suite1 extends FlatSpec{
"Test1 in Suite1" should "succeed" in{
succeed
}
}
and
class Suite2 extends FlatSpec{
"Test1 in Suite2" should "succeed" in{
succeed
}
}
Now let's add under the /project/ folder a CleanUp.scala object where our cleanup will live:
object CleanUp{
def cleanUp:Unit = println("Cleaning up after all suites are completed.")
}
That's a minimal example, actually you may have there whatever complex cleanup you need.
Now in our build.sbt let's add following:
(test in Test) := {
val testsResult = (test in Test).value
CleanUp.cleanUp
testsResult
}
(testOnly in Test) := {
(testOnly in Test).evaluated
CleanUp.cleanUp
}
This overrides the default behavior of the test and testOnly tasks so the cleanup will be applied after all suites (or all specified by the user suites) are executed.
For example, here is my sbt console check of the new testOnly:
[IJ]sbt:AfterAllTests> testOnly Suite1
[info] Suite1:
[info] Test1 in Suite1
[info] - should succeed
[info] Run completed in 150 milliseconds.
[info] Total number of tests run: 1
[info] Suites: completed 1, aborted 0
[info] Tests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.
Cleaning up after all suites are completed.
[success] Total time: 0 s, completed Dec 2, 2017 12:19:48 AM
[IJ]sbt:AfterAllTests>
And here is check of new test:
[IJ]sbt:AfterAllTests> test
[info] Suite2:
[info] Test1 in Suite2
[info] - should succeed
[info] Suite1:
[info] Test1 in Suite1
[info] - should succeed
[info] Run completed in 164 milliseconds.
[info] Total number of tests run: 2
[info] Suites: completed 2, aborted 0
[info] Tests: succeeded 2, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.
Cleaning up after all suites are completed.
[success] Total time: 2 s, completed Dec 2, 2017 12:28:25 AM
[IJ]sbt:AfterAllTests>
As you can see cleanup gets invoked.
Hope this helps.

org.scalajs.jsenv.ExternalJSEnv$NonZeroExitException: PhantomJS2 exited with code 2

I am using scalatest as my testing framework, here is my test code :
import org.scalatest.FunSuite
class SampleTest extends FunSuite{
test("hello") {
println(s"sdfsf")
}
}
build.sbt settings
val scalatestJS = libraryDependencies += "org.scalatest" %%% "scalatest" % Version.scalatest % Test
val scalatestJSSettings = Seq(
scalatestJS,
scalaJSStage in Global := FastOptStage,
// scalaJSStage in Global := FullOptStage,
// jsDependencies += RuntimeDOM,
// jsDependencies += ProvidedJS / "test-bundle.js" % Test,
jsEnv in Test := new PhantomJS2Env(scalaJSPhantomJSClassLoader.value, addArgs = Seq("--web-security=no"))
// jsEnv in Test := new NodeJSEnv()
)
Debug out :
[debug] Scala compilation took 0.961938628 s
[debug] Invalidating client/SampleTest.scala...
[debug] Invalidating (transitively) by inheritance from client/SampleTest.scala...
[debug] Initial set of included nodes: Set(client/SampleTest.scala)
[debug] Invalidated by transitive inheritance dependency: Set(client/SampleTest.scala)
[debug] The client/SampleTest.scala source file has the following implicit definitions changed:
[debug] unconstrainedEquality, convertToEqualizer.
[debug] All member reference dependencies will be considered within this context.
[debug] New invalidations:
[debug] Set()
[debug] Initial set of included nodes: Set()
[debug] Previously invalidated, but (transitively) depend on new invalidations:
[debug] Set()
[debug] All newly invalidated sources after taking into account (previously) recompiled sources:Set()
[debug] Copy resource mappings:
[debug]
[info] Fast optimizing client/assets/client-test-fastopt.js
[debug] Linker: Compute reachability: 710892 us
[debug] Linker: Assemble LinkedClasses: 445560 us
[debug] Basic Linking: 1168893 us
[debug] Inc. optimizer: Batch mode: true
[debug] Inc. optimizer: Incremental part: 127681 us
[debug] Inc. optimizer: Optimizing 11793 methods.
[debug] Inc. optimizer: Optimizer part: 5987357 us
[debug] Inc. optimizer: 6140114 us
[debug] Refiner: Compute reachability: 243271 us
[debug] Refiner: Assemble LinkedClasses: 27389 us
[debug] Refiner: 272567 us
[debug] Emitter: Class tree cache stats: reused: 0 -- invalidated: 1375
[debug] Emitter: Method tree cache stats: resued: 0 -- invalidated: 6643
[debug] Emitter (write output): 1353125 us
[debug] Global IR cache stats: reused: 0 -- invalidated: 130 -- trees read: 1748
[debug] Loading JSEnv with linked file client/assets/client-test-fastopt.js
org.scalajs.jsenv.ExternalJSEnv$NonZeroExitException: PhantomJS2 exited with code 2
at org.scalajs.jsenv.ExternalJSEnv$AbstractExtRunner.waitForVM(ExternalJSEnv.scala:107)
at org.scalajs.jsenv.ExternalJSEnv$ExtRunner.run(ExternalJSEnv.scala:156)
at org.scalajs.sbtplugin.FrameworkDetector.detect(FrameworkDetector.scala:66)
at org.scalajs.sbtplugin.ScalaJSPluginInternal$$anonfun$60.apply(ScalaJSPluginInternal.scala:737)
at org.scalajs.sbtplugin.ScalaJSPluginInternal$$anonfun$60.apply(ScalaJSPluginInternal.scala:720)
at scala.Function1$$anonfun$compose$1.apply(Function1.scala:47)
at sbt.$tilde$greater$$anonfun$$u2219$1.apply(TypeFunctions.scala:40)
at sbt.std.Transform$$anon$4.work(System.scala:63)
at sbt.Execute$$anonfun$submit$1$$anonfun$apply$1.apply(Execute.scala:226)
at sbt.Execute$$anonfun$submit$1$$anonfun$apply$1.apply(Execute.scala:226)
at sbt.ErrorHandling$.wideConvert(ErrorHandling.scala:17)
at sbt.Execute.work(Execute.scala:235)
at sbt.Execute$$anonfun$submit$1.apply(Execute.scala:226)
at sbt.Execute$$anonfun$submit$1.apply(Execute.scala:226)
at sbt.ConcurrentRestrictions$$anon$4$$anonfun$1.apply(ConcurrentRestrictions.scala:159)
at sbt.CompletionService$$anon$2.call(CompletionService.scala:28)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
[error] (client/test:loadedTestFrameworks) org.scalajs.jsenv.ExternalJSEnv$NonZeroExitException: PhantomJS2 exited with code 2
[error] Total time: 15 s, completed May 20, 2016 12:20:29 AM
Note : This is used to work fine but not anymore :s
Edit :
ScalaTest : 3.0.0-M15
ScalaJS : 0.6.9
Scala : 2.11.8
Phantom JS : 2.0.0
In my code i have a class which extends js class
#ScalaJSDefined
class MyLab(container : dom.Node) extends JSLab(container) {... }
even though i am not using this class in my tests,looks like source code generated for this line is some how looking for JSLab in global! which causes crash of phantomjs. Interestingly tests are executing fine on Rhino :s. anyhow i added JSLab as test dependency and error gone.
I don't know whom to blame here , whether its stupid developer(me) who went in different direction while solving problem or scala.js for not helpful in why phantomjs crashed :s

How to run ScalaTest with Guice DI and Slick?

I don't know how to configure GuiceApplicationBuilder in such a way, that I am able to load controllers that require a DatabaseConfigProvider to be injected.
I'd like to specify an alternative postgres database for testing, or an in memory database (if that is possible).
Code
class User
extends MySpecs
with OneAppPerTest
{
override def newAppForTest( testData: TestData ) = new GuiceApplicationBuilder()
// Somehow bind a database here, I guess?
.build()
"A test" should "test" in
{
val result = Application.instanceCache[api.controller.User]
.apply( app )
.list()( FakeRequest() )
...
}
}
Stacktrace
[info] - should return an entity *** FAILED ***
[info] com.google.inject.ConfigurationException: Guice configuration errors:
[info]
[info] 1) No implementation for play.api.db.slick.DatabaseConfigProvider was bound.
[info] while locating play.api.db.slick.DatabaseConfigProvider
[info] for parameter 1 at api.controller.User.<init>(User.scala:22)
[info] while locating api.controller.User
[info]
[info] 1 error
[info] at com.google.inject.internal.InjectorImpl.getProvider(InjectorImpl.java:1042)
[info] at com.google.inject.internal.InjectorImpl.getProvider(InjectorImpl.java:1001)
[info] at com.google.inject.internal.InjectorImpl.getInstance(InjectorImpl.java:1051)
[info] at play.api.inject.guice.GuiceInjector.instanceOf(GuiceInjectorBuilder.scala:321)
[info] at play.api.inject.guice.GuiceInjector.instanceOf(GuiceInjectorBuilder.scala:316)
[info] at play.api.Application$$anonfun$instanceCache$1.apply(Application.scala:234)
[info] at play.api.Application$$anonfun$instanceCache$1.apply(Application.scala:234)
[info] at play.utils.InlineCache.fresh(InlineCache.scala:69)
[info] at play.utils.InlineCache.apply(InlineCache.scala:55)
[info] ...
You need to add a configuration to your GuiceApplicationBuilder(), then everything should be handled automatically by play framework. Something like this should help:
val app = new GuiceApplicationBuilder()
.configure(
Configuration.from(
Map(
"slick.dbs.YOURDBNAME.driver" -> "slick.driver.H2Driver$",
"slick.dbs.YOURDBNAME.db.driver" -> "org.h2.Driver",
"slick.dbs.YOURDBNAME.db.url" -> "jdbc:h2:mem:",
"slick.dbs.default.driver" -> "slick.driver.MySQLDriver$",
"slick.dbs.default.db.driver" -> "com.mysql.jdbc.Driver"
)
)
)
.in(Mode.Test)
.build()
There is a little bit of setting up in this approach but the final result is fair. First of all start with implementing your own GuiceApplicationLoader by extending it. See my answer how to implement it. Why your own application loader? You can specify different configs/modules per Prod/Dev/Test modes - as well as different data sources. Your main application.conf wouldn't have data source configured. Instead, you would move it to environment specific configurations which would be merged with main configuration by application loader anyway. Your dev.conf would look as follows:
slick.dbs {
default {
driver = "slick.driver.PostgresDriver$",
db {
driver = "org.postgresql.Driver",
url = "jdbc:postgresql://localhost:5432/dev",
user = "postgres"
password = "postgres"
}
}
}
And the trick now is to use same data source name, in this case default, for all other configurations (the database url, drivers, credentials etc. would be different). With such setup, your evolutions will be applied to your test and dev database. Your test.conf could look as follows:
slick.dbs {
default {
// in memory configuration
}
}
In your tests, use WithApplicationLoader with your custom application loader instead and thats it.
#RunWith(classOf[JUnitRunner])
class ApplicationSpec extends Specification {
"Application" should {
"return text/html ok for home" in new WithApplicationLoader(new CustomApplicationLoader) {
val home = route(FakeRequest(routes.ApplicationController.home())).get
status(home) must equalTo(OK)
contentType(home) must beSome.which(_ == "text/html")
}
}
}
Within the test itself, you have an access to app: Application value:
val service = app.injector.instanceOf(classOf[IService])