4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/main/resources/mojoResources.properties:
--------------------------------------------------------------------------------
1 | reporter.mojo.name = ScalaTest Results
2 | reporter.mojo.description = The test results from the ScalaTest run
3 | reporter.mojo.outputTitle = ScalaTest Results
--------------------------------------------------------------------------------
/src/it/redirect-output/invoker.properties:
--------------------------------------------------------------------------------
1 | # A comma or space separated list of goals/phases to execute, may
2 | # specify an empty list to execute the default goal of the IT project
3 | invoker.goals = clean test
4 |
--------------------------------------------------------------------------------
/src/it/spaces in path/invoker.properties:
--------------------------------------------------------------------------------
1 | # A comma or space separated list of goals/phases to execute, may
2 | # specify an empty list to execute the default goal of the IT project
3 | invoker.goals = clean test
4 |
--------------------------------------------------------------------------------
/src/it/lift/src/main/scala/demo/helloworld/snippet/HelloWorld.scala:
--------------------------------------------------------------------------------
1 | package demo.helloworld.snippet
2 |
3 | class HelloWorld {
4 | def howdy = Welcome to helloworld at {new _root_.java.util.Date}
5 | }
6 |
7 |
--------------------------------------------------------------------------------
/src/it/redirect-output/README.md:
--------------------------------------------------------------------------------
1 | # redirect output
2 |
3 | This project is part of scalatest-maven-plugin's suite of integration tests. It is meant to check that scalatest-maven-plugin can run for projects that use test output redirect.
--------------------------------------------------------------------------------
/src/it/spaces in path/README.md:
--------------------------------------------------------------------------------
1 | # spaces in path
2 |
3 | This project is part of scalatest-maven-plugin's suite of integration tests. It is meant to check that scalatest-maven-plugin can run for projects that contain spaces in their file path.
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: java
2 |
3 | sudo: false
4 |
5 | jdk:
6 | - oraclejdk8
7 |
8 | cache:
9 | directories:
10 | - $HOME/.m2
11 |
12 | # skip mvn install
13 | install: true
14 |
15 | script: mvn clean package invoker:install invoker:run
16 |
--------------------------------------------------------------------------------
/src/site/site.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/it/redirect-output/src/test/scala/org/example/AppTest.scala:
--------------------------------------------------------------------------------
1 | package org.example
2 |
3 | import org.scalatest.flatspec.AnyFlatSpec
4 | import org.scalatest.matchers.should.Matchers
5 |
6 | class AppTest extends AnyFlatSpec with Matchers {
7 | "Our example App" should "run" in {
8 | val app = new App
9 | app.runApp() shouldBe "It ran!"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/it/spaces in path/src/test/scala/org/example/AppTest.scala:
--------------------------------------------------------------------------------
1 | package org.example
2 |
3 | import org.scalatest.flatspec.AnyFlatSpec
4 | import org.scalatest.matchers.should.Matchers
5 |
6 | class AppTest extends AnyFlatSpec with Matchers {
7 | "Our example App" should "run" in {
8 | val app = new App
9 | app.runApp() shouldBe "It ran!"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/.github/workflows/maven.yml:
--------------------------------------------------------------------------------
1 | name: scalatest-maven-plugin
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | verify:
7 |
8 | runs-on: ubuntu-latest
9 |
10 | steps:
11 | - name: git
12 | uses: actions/checkout@v2
13 |
14 | - name: java
15 | uses: actions/setup-java@v3
16 | with:
17 | java-version: '8'
18 | distribution: 'temurin'
19 |
20 | - name: maven
21 | run: mvn -B clean verify --file pom.xml
22 |
--------------------------------------------------------------------------------
/src/it/lift/src/test/scala/LiftConsole.scala:
--------------------------------------------------------------------------------
1 | import _root_.bootstrap.liftweb.Boot
2 | import _root_.scala.tools.nsc.MainGenericRunner
3 |
4 | object LiftConsole {
5 | def main(args : Array[String]) {
6 | // Instantiate your project's Boot file
7 | val b = new Boot()
8 | // Boot your project
9 | b.boot
10 | // Now run the MainGenericRunner to get your repl
11 | MainGenericRunner.main(args)
12 | // After the repl exits, then exit the scala script
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/changes/changes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Maven ScalaTest Plugin
4 |
5 |
6 |
7 | Created initial version. Jon gets credit for most of the work in this release.
8 | Updated to work with Scala 2.8 and cleaned up for release.
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/it/no-scalatest-ignore/verify.groovy:
--------------------------------------------------------------------------------
1 |
2 | def logsFile = new File(basedir, "build.log")
3 |
4 | if (!logsFile.exists()) {
5 | throw new Exception("Could not find build.log. Searched: " + logsFile)
6 | }
7 |
8 | def result = logsFile.readLines().findAll() {
9 | it.contains("Skipped because ScalaTest not available on classpath.")
10 | }
11 | if (result.size == 0) {
12 | throw new Exception("Could not find skipped because ScalaTest not available on classpath message in build.log")
13 | }
14 |
15 | return true
16 |
--------------------------------------------------------------------------------
/src/it/lift/src/main/webapp/templates-hidden/default.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | demo.helloworld:helloworld:1.0-SNAPSHOT
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/it/lift/src/main/webapp/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
9 | LiftFilter
10 | Lift Filter
11 | The Filter that intercepts lift calls
12 | net.liftweb.http.LiftFilter
13 |
14 |
15 |
16 |
17 | LiftFilter
18 | /*
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/it/lift/src/main/scala/bootstrap/liftweb/Boot.scala:
--------------------------------------------------------------------------------
1 | package bootstrap.liftweb
2 |
3 | import _root_.net.liftweb.util._
4 | import _root_.net.liftweb.http._
5 | import _root_.net.liftweb.sitemap._
6 | import _root_.net.liftweb.sitemap.Loc._
7 | import Helpers._
8 |
9 | /**
10 | * A class that's instantiated early and run. It allows the application
11 | * to modify lift's environment
12 | */
13 | class Boot {
14 | def boot {
15 | // where to search snippet
16 | LiftRules.addToPackages("demo.helloworld")
17 |
18 | // Build SiteMap
19 | val entries = Menu(Loc("Home", List("index"), "Home")) :: Nil
20 | LiftRules.setSiteMap(SiteMap(entries:_*))
21 | }
22 | }
23 |
24 |
--------------------------------------------------------------------------------
/src/it/lift/src/test/scala/RunWebApp.scala:
--------------------------------------------------------------------------------
1 | import _root_.org.mortbay.jetty.Connector
2 | import _root_.org.mortbay.jetty.Server
3 | import _root_.org.mortbay.jetty.webapp.WebAppContext
4 |
5 | object RunWebApp extends App {
6 | val server = new Server(8080)
7 | val context = new WebAppContext()
8 | context.setServer(server)
9 | context.setContextPath("/")
10 | context.setWar("src/main/webapp")
11 |
12 | server.addHandler(context)
13 |
14 | try {
15 | println(">>> STARTING EMBEDDED JETTY SERVER, PRESS ANY KEY TO STOP")
16 | server.start()
17 | while (System.in.available() == 0) {
18 | Thread.sleep(5000)
19 | }
20 | server.stop()
21 | server.join()
22 | } catch {
23 | case exc : Exception => {
24 | exc.printStackTrace()
25 | System.exit(100)
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/it/settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | it-repo
6 |
7 | true
8 |
9 |
10 |
11 | local.central
12 | @localRepositoryUrl@
13 |
14 | true
15 |
16 |
17 | true
18 |
19 |
20 |
21 |
22 |
23 | local.central
24 | @localRepositoryUrl@
25 |
26 | true
27 |
28 |
29 | true
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/src/it/lift/verify.groovy:
--------------------------------------------------------------------------------
1 |
2 | def logsFile = new File(basedir, "build.log")
3 |
4 | if (!logsFile.exists()) {
5 | throw new Exception("Could not find build.log. Searched: " + logsFile)
6 | }
7 |
8 | def testSummaryLines = []
9 |
10 | logsFile.filterLine {
11 | it ==~ /.*Tests: succeeded [0-9]*, failed [0-9]*, canceled [0-9]*, ignored [0-9]*, pending [0-9]*.*/
12 | }.each { line -> testSummaryLines << "" + line }
13 |
14 | if (testSummaryLines.size == 0) {
15 | throw new Exception("Could not find scalatest's summary line in build.log")
16 | } else if (testSummaryLines.size > 1) {
17 | throw new Exception("Found more than one scalatest summary line in build.log")
18 | }
19 |
20 | def theLine = testSummaryLines[0]
21 |
22 | if (theLine.isEmpty()) {
23 | throw new Exception("Could not find scalatest's non empty summary line in build.log")
24 | }
25 |
26 | if (theLine.contains("Tests: succeeded 0, failed 0, canceled 0, ignored 0, pending 0")) {
27 | throw new Exception("No tests were run by scalatest!")
28 | }
29 |
30 | return true
31 |
--------------------------------------------------------------------------------
/src/it/spaces in path/verify.groovy:
--------------------------------------------------------------------------------
1 |
2 | def logsFile = new File(basedir, "build.log")
3 |
4 | if (!logsFile.exists()) {
5 | throw new Exception("Could not find build.log. Searched: " + logsFile)
6 | }
7 |
8 | def testSummaryLines = []
9 |
10 | logsFile.filterLine {
11 | it ==~ /.*Tests: succeeded [0-9]*, failed [0-9]*, canceled [0-9]*, ignored [0-9]*, pending [0-9]*.*/
12 | }.each { line -> testSummaryLines << "" + line }
13 |
14 | if (testSummaryLines.size == 0) {
15 | throw new Exception("Could not find scalatest's summary line in build.log")
16 | } else if (testSummaryLines.size > 1) {
17 | throw new Exception("Found more than one scalatest summary line in build.log")
18 | }
19 |
20 | def theLine = testSummaryLines[0]
21 |
22 | if (theLine.isEmpty()) {
23 | throw new Exception("Could not find scalatest's non empty summary line in build.log")
24 | }
25 |
26 | if (theLine.contains("Tests: succeeded 0, failed 0, canceled 0, ignored 0, pending 0")) {
27 | throw new Exception("No tests were run by scalatest!")
28 | }
29 |
30 | return true
31 |
--------------------------------------------------------------------------------
/src/it/redirect-output/verify.groovy:
--------------------------------------------------------------------------------
1 |
2 | def reportsDir = new File(basedir, "target" + File.separator + "scalatest-reports")
3 | def logsFile = new File(reportsDir, "scalatest-output.txt")
4 |
5 | if (!logsFile.exists()) {
6 | throw new Exception("Could not find output.txt. Searched: " + logsFile)
7 | }
8 |
9 | def testSummaryLines = []
10 |
11 | logsFile.filterLine {
12 | it ==~ /.*Tests: succeeded [0-9]*, failed [0-9]*, canceled [0-9]*, ignored [0-9]*, pending [0-9]*.*/
13 | }.each { line -> testSummaryLines << "" + line }
14 |
15 | if (testSummaryLines.size == 0) {
16 | throw new Exception("Could not find scalatest's summary line in output.txt")
17 | } else if (testSummaryLines.size > 1) {
18 | throw new Exception("Found more than one scalatest summary line in output.txt")
19 | }
20 |
21 | def theLine = testSummaryLines[0]
22 |
23 | if (theLine.isEmpty()) {
24 | throw new Exception("Could not find scalatest's non-empty summary line in output.txt")
25 | }
26 |
27 | if (theLine.contains("Tests: succeeded 0, failed 0, canceled 0, ignored 0, pending 0")) {
28 | throw new Exception("No tests were run by scalatest!")
29 | }
30 |
31 | return true
32 |
--------------------------------------------------------------------------------
/src/test/scala/org/scalatest/tools/maven/MojoUtilsTest.scala:
--------------------------------------------------------------------------------
1 | package org.scalatest.tools.maven
2 |
3 | import org.junit.contrib.java.lang.system.{ClearSystemProperties, EnvironmentVariables}
4 | import org.junit.{Rule, Test}
5 |
6 | class MojoUtilsTest {
7 | val _env: EnvironmentVariables = new EnvironmentVariables()
8 | val _sys: ClearSystemProperties = new ClearSystemProperties("java.home")
9 |
10 | @Rule
11 | def env: EnvironmentVariables = _env
12 |
13 | @Rule
14 | def sys: ClearSystemProperties = _sys
15 |
16 | @Test
17 | def getJvmHappyPath(): Unit = {
18 | env.clear("JAVA_HOME")
19 | System.setProperty("java.home", "/test/jvm")
20 | assert(MojoUtils.getJvm == "/test/jvm/bin/java")
21 | }
22 |
23 | @Test
24 | def getJvmWithoutJavaHome(): Unit = {
25 | env.clear("JAVA_HOME")
26 | assert(MojoUtils.getJvm == "java")
27 | }
28 |
29 | @Test
30 | def getJvmFromEnvironment(): Unit = {
31 | env.clear("JAVA_HOME")
32 | env.set("JAVA_HOME", "/opt/jdk-11")
33 | Console.print(MojoUtils.getJvm)
34 | assert(MojoUtils.getJvm == "/opt/jdk-11/bin/java")
35 | }
36 |
37 | @Test
38 | def getJvmJavaHomeIsPriority(): Unit = {
39 | System.setProperty("java.home", "/test/jvm")
40 | env.set("JAVA_HOME", "/opt/jdk-11")
41 | assert(MojoUtils.getJvm == "/test/jvm/bin/java")
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/it/manual/gui/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 | com.jteigen
6 | maven-scalatest-plugin-it-gui
7 | jar
8 | 1.0-SNAPSHOT
9 | Manual gui test
10 |
11 |
12 | maven.scalatest.plugin.its
13 | it-parent
14 | 1.0-SNAPSHOT
15 | ../it-parent
16 |
17 |
18 |
19 |
20 | org.scalatest
21 | scalatest
22 | 1.2
23 | test
24 |
25 |
26 | org.scala-lang
27 | scala-library
28 | 2.8.1
29 |
30 |
31 |
32 |
33 |
34 |
35 | org.scalatest
36 | maven-scalatest-plugin
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/src/test/scala/org/scalatest/tools/maven/PluginMatchers.scala:
--------------------------------------------------------------------------------
1 | package org.scalatest.tools.maven
2 |
3 | import org.scalatest.matchers.{MatchResult, Matcher}
4 |
5 | /**
6 | * @author Jon-Anders Teigen
7 | */
8 | trait PluginMatchers {
9 | def containSlice(slice: String*) = new Matcher[Array[String]] {
10 | def apply(seq: Array[String]) = {
11 | MatchResult(
12 | seq containsSlice slice,
13 | "The " + seq + " did not contain the slice " + slice,
14 | "The " + seq + " did contain the slice " + slice
15 | )
16 | }
17 | }
18 |
19 | def containCompoundArgs(name:String, values:String*) = new Matcher[Array[String]] {
20 | def apply(seq: Array[String]) = {
21 | MatchResult(
22 | seq containsSlice List(name,values.mkString(" ")),
23 | "The compoundArgs for " + name + " and " + values.mkString(" ") + " where not found in " + seq,
24 | "The compoundArgs for " + name + " and " + values.mkString(" ") + " where found in " + seq
25 | )
26 | }
27 | }
28 |
29 | def containSuiteArgs(name:String, values:String*) = new Matcher[Array[String]] {
30 | def apply(seq: Array[String]) = {
31 | MatchResult(
32 | values.forall(v => seq.containsSlice(List(name, v))),
33 | "The suiteArgs " + name +" "+ values.mkString(" ") + " were not all found in " + seq,
34 | "The suiteArgs for " + name +" "+ values.mkString(" ") + " were all found in " + seq
35 | )
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/it/it-parent/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 | maven.scalatest.plugin.its
5 | it-parent
6 | 1.0-SNAPSHOT
7 | pom
8 | it-parent
9 |
10 |
11 |
12 | maven-3
13 |
14 |
15 |
16 | ${basedir}
17 |
18 |
19 |
20 |
21 |
22 |
23 | org.apache.maven.plugins
24 | maven-site-plugin
25 | 3.5.1
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | org.scalatest
38 | scalatest-maven-plugin
39 | @project.version@
40 |
41 |
42 | org.apache.maven.plugins
43 | maven-project-info-reports-plugin
44 | @maven.project.info.reports.plugin.version@
45 |
46 |
47 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/src/it/no-scalatest-ignore/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 | org.scalatest
6 | no-scalatest-ignore
7 | jar
8 | 1.0-SNAPSHOT
9 | No ScalaTest Ignore configuration test
10 |
11 |
12 |
13 | org.scala-lang
14 | scala-library
15 | @scala.version@
16 |
17 |
18 |
19 |
20 |
21 |
22 | org.scalatest
23 | scalatest-maven-plugin
24 | @project.version@
25 |
26 | true
27 |
28 |
29 |
30 | test
31 |
32 | test
33 |
34 |
35 |
36 |
37 |
38 | org.apache.maven.plugins
39 | maven-project-info-reports-plugin
40 | @maven.project.info.reports.plugin.version@
41 |
42 |
43 | org.apache.maven.plugins
44 | maven-surefire-plugin
45 | 2.7
46 |
47 | true
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/notes.txt:
--------------------------------------------------------------------------------
1 | Work done so far
2 | ----------------
3 | 1. Moved code from github to google code under "trenchguinea" branch
4 | 2. Integration tests used to contain a copy of the scalatestexamples from ScalaTest 1.0-snapshot, which didn't compile anymore,
5 | so I changed them to auto-export from SCM in the generate-test-sources phase into the target/generated/src/test folder.
6 | a. I placed into target so that a clean would be sure to copy down a clean set of the examples
7 | b. The way the tests were executed from the Runner wasn't entirely valid to make them pass so fixed them
8 | 3. Did other prep for release like release notes, JavaDoc and Source XRef reports, and the Cobertura code-coverage report
9 |
10 |
11 | To Use
12 | ------
13 | 1. Disable maven-surefire-plugin by setting skipTests=true
14 | 2. Add scalatest-maven-plugin to section and configure the "test" goal in an execution
15 | 3. Execute "test" phase or directly execute the scalatest:test or scalatest:gui goals
16 |
17 |
18 | Future implementation thoughts
19 | ------------------------------
20 | 1. Could actually integrate with Surefire directly by implementing SurefireProvider API
21 | a. The current method of disabling surefire to replace with scalatest is a bit of a hack and drops features that surefire would expect
22 | b. May allow the surefire-report-plugin "just work", though not sure about this
23 | c. This would require a lot of the discovery logic currently in Runner to be externally-consumable so that Surefire can discover the tests
24 | 2. To really be a replacement for surefire we'd need a replacement for the surefire-report-plugin. Users commonly use the surefire output
25 | for verifying process compliance.
26 | a. We could write a report goal that just dumps the text to a file and links that file into the site but it's far from ideal
27 | b. Once the HTML/Flex reporter is available that the report goal executes that reporter (at least by default)
28 |
--------------------------------------------------------------------------------
/src/it/lift/src/test/scala/demo/helloworld/AppTest.scala:
--------------------------------------------------------------------------------
1 | package demo.helloworld
2 |
3 | import _root_.java.io.File
4 | import _root_.org.scalatest.funsuite.AnyFunSuite
5 | import _root_.scala.xml.XML
6 | import _root_.net.liftweb.common.Full
7 | import _root_.net.liftweb.util.PCDataXmlParser
8 |
9 | /**
10 | * Unit test for simple App.
11 | */
12 | class AppTest extends AnyFunSuite {
13 |
14 | /**
15 | * Rigourous Tests :-)
16 | */
17 | test("OK"){
18 | assert(true === true)
19 | }
20 |
21 | ignore("KO"){
22 | assert(true === false)
23 | }
24 |
25 | /**
26 | * Tests to make sure the project's XML files are well-formed.
27 | *
28 | * Finds every *.html and *.xml file in src/main/webapp (and its
29 | * subdirectories) and tests to make sure they are well-formed.
30 | */
31 | test("xml"){
32 | var failed: List[File] = Nil
33 |
34 | def handledXml(file: String) =
35 | file.endsWith(".xml")
36 |
37 | def handledXHtml(file: String) =
38 | file.endsWith(".html") || file.endsWith(".htm") || file.endsWith(".xhtml")
39 |
40 | def wellFormed(file: File) {
41 | if (file.isDirectory)
42 | for (f <- file.listFiles) wellFormed(f)
43 |
44 | if (file.isFile && handledXml(file.getName)) {
45 | try {
46 | val f = javax.xml.parsers.SAXParserFactory.newInstance()
47 | f.setFeature("http://apache.org/xml/features/disallow-doctype-decl", false)
48 | val p = f.newSAXParser()
49 | XML.withSAXParser(p).loadFile(file)
50 | } catch {
51 | case e: _root_.org.xml.sax.SAXParseException =>
52 | e.printStackTrace()
53 | failed = file :: failed
54 | }
55 | }
56 | if (file.isFile && handledXHtml(file.getName)) {
57 | PCDataXmlParser(new java.io.FileInputStream(file.getAbsolutePath)) match {
58 | case Full(_) => // file is ok
59 | case _ => failed = file :: failed
60 | }
61 | }
62 | }
63 |
64 | wellFormed(new File("src/main/webapp"))
65 |
66 | val numFails = failed.size
67 | if (numFails > 0) {
68 | val fileStr = if (numFails == 1) "file" else "files"
69 | val msg = "Malformed XML in " + numFails + " " + fileStr + ": " + failed.mkString(", ")
70 | println(msg)
71 | fail(msg)
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ScalaTest Maven Plugin
2 | ======================
3 |
4 | Building ScalaTest Maven Plugin
5 | -------------------------------
6 |
7 | Maven 3 is used to manage the build process. To build this plugin, please make sure you have the following installed:-
8 |
9 | * The Git command line tools
10 | * A recent JDK (the [current Oracle JDK](http://www.oracle.com/technetwork/java/javase/downloads/index.html) is recommended)
11 | * Maven 3 (http://maven.apache.org/download.html)
12 |
13 | You then clone and checkout master trunk:-
14 |
15 | $ git clone git://github.com/scalatest/scalatest-maven-plugin.git
16 |
17 | $ cd scalatest-maven-plugin
18 |
19 | Finally use the following commands to build for ScalaTest Maven Plugin:
20 |
21 | $ mvn clean package
22 |
23 | The built output will be available in target/.
24 |
25 | Using ScalaTest Maven Plugin
26 | ----------------------------
27 |
28 | To use the ScalaTest Maven plugin, you need to disable SureFire and enable ScalaTest. Here's an example of how to do this in your pom.xml:
29 |
30 |
62 |
63 | org.apache.maven.plugins
64 | maven-surefire-plugin
65 | 2.7
66 |
67 | true
68 |
69 |
70 |
71 |
72 | org.scalatest
73 | scalatest-maven-plugin
74 |
75 | true
76 |
77 |
78 |
79 |
80 | test
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 | org.scalatest
91 | scalatest-maven-plugin
92 |
93 |
94 |
95 |
96 |
--------------------------------------------------------------------------------
/src/it/redirect-output/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 | redirect-output-it
5 | 1.0-SNAPSHOT
6 | jar
7 | redirect-output
8 | Runs the plugin for a project that uses test output redirect.
9 |
10 |
11 | maven.scalatest.plugin.its
12 | it-parent
13 | 1.0-SNAPSHOT
14 | ../it-parent
15 |
16 |
17 |
18 |
19 | org.scala-lang
20 | scala-library
21 | @scala.version@
22 |
23 |
24 | org.scalatest
25 | scalatest_@scala.major.version@
26 | @scalatest.version@
27 | test
28 |
29 |
30 |
31 |
32 | src/main/scala
33 | src/test/scala
34 |
35 |
36 | net.alchim31.maven
37 | scala-maven-plugin
38 | @scala.maven.plugin.version@
39 |
40 | incremental
41 | ${scala.version}
42 |
43 |
44 |
45 | scala-compile
46 | process-resources
47 |
48 | add-source
49 | compile
50 |
51 |
52 |
53 | scala-test-compile
54 | process-test-resources
55 |
56 | testCompile
57 |
58 |
59 |
60 |
61 |
62 |
63 | org.apache.maven.plugins
64 | maven-surefire-plugin
65 | 2.7
66 |
67 | true
68 |
69 |
70 |
71 |
72 | org.scalatest
73 | scalatest-maven-plugin
74 |
75 | true
76 | true
77 |
78 |
79 |
80 |
81 | test
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 | org.scalatest
92 | scalatest-maven-plugin
93 |
94 |
95 |
96 |
97 |
--------------------------------------------------------------------------------
/src/main/java/org/scalatest/tools/maven/MojoUtils.java:
--------------------------------------------------------------------------------
1 | package org.scalatest.tools.maven;
2 |
3 | import java.io.File;
4 | import java.util.ArrayList;
5 | import java.util.List;
6 |
7 | import static org.apache.commons.lang3.StringUtils.isEmpty;
8 |
9 | import static java.util.Collections.unmodifiableList;
10 |
11 | /**
12 | * Provides internal utilities for the Mojo's operations.
13 | *
14 | * @author Jon-Anders Teigen
15 | */
16 | final class MojoUtils {
17 | private MojoUtils() {
18 | }
19 |
20 | static interface F {
21 | public String f(String in);
22 | }
23 |
24 | static F passThrough = new F() {
25 | public String f(String in) {
26 | return in;
27 | }
28 | };
29 |
30 | static F fileRelativeTo(final File relative) {
31 | return new F() {
32 | public String f(String in) {
33 | File file = new File(relative, in);
34 | File parentDir = file.getParentFile();
35 | createIfNotExists(parentDir); // sideeffect!
36 | return file.getAbsolutePath();
37 | }
38 | };
39 | }
40 |
41 | static F dirRelativeTo(final File relative){
42 | return new F(){
43 | public String f(String in){
44 | File dir = new File(relative, in);
45 | createIfNotExists(dir); // sideeffect!
46 | return dir.getAbsolutePath();
47 | }
48 | };
49 | }
50 |
51 | // sideeffect!
52 | private static synchronized void createIfNotExists(File dir) {
53 | if(!dir.exists() && !dir.mkdirs()){
54 | throw new IllegalStateException("Cannot create directory " + dir);
55 | }
56 | }
57 |
58 | static List compoundArg(String name, String...strings){
59 | List list = new ArrayList();
60 | List params = new ArrayList();
61 | for(String commaSeparated : strings){
62 | params.addAll(splitOnComma(commaSeparated));
63 | }
64 | if (params.size() > 0) {
65 | list.add(name);
66 | String prefix = "";
67 | String a = "";
68 | for (String param : params) {
69 | a += prefix;
70 | a += param;
71 | prefix = " ";
72 | }
73 | list.add(a);
74 | }
75 | return unmodifiableList(list);
76 | }
77 |
78 | static List suiteArg(String name, String commaSeparated) {
79 | List list = new ArrayList();
80 | for (String param : splitOnComma(commaSeparated)) {
81 | list.add(name);
82 | list.add(param);
83 | }
84 | return unmodifiableList(list);
85 | }
86 |
87 | static List reporterArg(String name, String commaSeparated, F map) {
88 | List r = new ArrayList();
89 | for (String arg : splitOnComma(commaSeparated)) {
90 | String[] split = arg.split("\\s");
91 | if (split.length == 1) {
92 | r.add(name);
93 | r.add(map.f(split[0]));
94 | } else {
95 | r.add(name + split[0]);
96 | r.add(map.f(split[1]));
97 | }
98 | }
99 | return unmodifiableList(r);
100 | }
101 |
102 | //
103 | // Splits a comma-delimited string. Supports backslash escapes for
104 | // commas where string should not be split. E.g. "a, b, c" returns
105 | // list ("a", "b", "c"), but "a\, b, c" returns ("a, b", "c").
106 | //
107 | static List splitOnComma(String cs) {
108 | List args = new ArrayList();
109 | if (cs == null) {
110 | return unmodifiableList(args);
111 | } else {
112 | String[] split = cs.split("(?...lists){
121 | List c = new ArrayList();
122 | for(List l : lists){
123 | c.addAll(l);
124 | }
125 | return c.toArray(new String[c.size()]);
126 | }
127 |
128 | private static String getJavaHome() {
129 | final String result;
130 | if (!isEmpty(System.getProperty("java.home"))) {
131 | result = System.getProperty("java.home");
132 | }
133 | else if (!isEmpty(System.getenv("JAVA_HOME"))) {
134 | result = System.getenv("JAVA_HOME");
135 | } else {
136 | result = null;
137 | }
138 | return result;
139 | }
140 |
141 | static String getJvm() {
142 | final String jh = getJavaHome();
143 | final String result;
144 | if (jh == null) {
145 | result = "java";
146 | } else {
147 | result = jh + File.separator + "bin" + File.separator + "java";
148 | }
149 | return result;
150 | }
151 |
152 | static String stripNewLines(String argLine) {
153 | return argLine.replaceAll("[\r\n]{1,2}", " ");
154 | }
155 | }
156 |
--------------------------------------------------------------------------------
/src/it/lift/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 | maven.scalatest.plugin.its
5 | lift-it
6 | 1.0-SNAPSHOT
7 | war
8 | lift-it
9 | Runs the plugin against a basic Lift application.
10 |
11 |
12 | 3.2.0
13 |
14 |
15 |
16 | maven.scalatest.plugin.its
17 | it-parent
18 | 1.0-SNAPSHOT
19 | ../it-parent
20 |
21 |
22 |
23 |
24 | org.scala-lang
25 | scala-library
26 | @scala.version@
27 |
28 |
29 | net.liftweb
30 | lift-util_@scala.major.version@
31 | ${lift.version}
32 |
33 |
34 | net.liftweb
35 | lift-webkit_@scala.major.version@
36 | ${lift.version}
37 |
38 |
39 | net.liftweb
40 | lift-mapper_@scala.major.version@
41 | ${lift.version}
42 |
43 |
44 | javax.servlet
45 | servlet-api
46 | 2.5
47 | provided
48 |
49 |
50 | org.scalatest
51 | scalatest_@scala.major.version@
52 | @scalatest.version@
53 | test
54 |
55 |
56 | org.mortbay.jetty
57 | jetty
58 | [6.1.6,)
59 | test
60 |
61 |
62 |
63 | org.scala-lang
64 | scala-compiler
65 | @scala.version@
66 | test
67 |
68 |
69 |
70 |
71 | src/main/scala
72 | src/test/scala
73 |
74 |
75 | net.alchim31.maven
76 | scala-maven-plugin
77 | @scala.maven.plugin.version@
78 |
79 | incremental
80 | ${scala.version}
81 |
82 |
83 |
84 | scala-compile
85 | process-resources
86 |
87 | add-source
88 | compile
89 |
90 |
91 |
92 | scala-test-compile
93 | process-test-resources
94 |
95 | testCompile
96 |
97 |
98 |
99 |
100 |
101 | org.mortbay.jetty
102 | maven-jetty-plugin
103 | 6.1.26
104 |
105 | /
106 | 5
107 |
108 |
109 |
110 | net.sf.alchim
111 | yuicompressor-maven-plugin
112 | 0.7.1
113 |
114 |
115 |
116 | compress
117 |
118 |
119 |
120 |
121 | true
122 |
123 |
124 |
125 |
126 | org.apache.maven.plugins
127 | maven-surefire-plugin
128 | 2.7
129 |
130 | true
131 |
132 |
133 |
134 |
135 | org.scalatest
136 | scalatest-maven-plugin
137 |
138 |
139 |
140 | test
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 | org.scalatest
151 | scalatest-maven-plugin
152 |
153 |
154 |
155 |
156 |
--------------------------------------------------------------------------------
/src/main/java/org/scalatest/tools/maven/ReporterMojo.java:
--------------------------------------------------------------------------------
1 | package org.scalatest.tools.maven;
2 |
3 | import org.codehaus.doxia.sink.Sink;
4 | import org.apache.maven.plugin.MojoExecutionException;
5 | import org.apache.maven.plugin.MojoFailureException;
6 | import org.apache.maven.reporting.MavenReport;
7 | import org.apache.maven.reporting.MavenReportException;
8 |
9 | import java.io.*;
10 | import java.util.List;
11 | import java.util.Locale;
12 | import java.util.ResourceBundle;
13 |
14 | import static org.scalatest.tools.maven.MojoUtils.*;
15 |
16 | /**
17 | * A reporting mojo to capture the ScalaTest output as a file that integrates into the Maven site of a project.
18 | *
19 | * @author Jon-Anders Teigen
20 | * @author Sean Griffin
21 | * @author Mike Pilquist
22 | * @author Bill Venners
23 | * @phase site
24 | * @goal reporter
25 | */
26 | public class ReporterMojo extends AbstractScalaTestMojo implements MavenReport {
27 |
28 | /**
29 | * Directory where reports will go.
30 | *
31 | * @parameter property="project.reporting.outputDirectory"
32 | * @required
33 | * @readonly
34 | */
35 | private File reportingOutputDirectory;
36 |
37 | /**
38 | * Consists of an optional configuration parameters for the file reporter.
39 | * For more info on configuring reporters, see the ScalaTest documentation.
40 | * @parameter property="fileReporterOptions"
41 | */
42 | private String fileReporterOptions;
43 |
44 | public void execute() throws MojoExecutionException, MojoFailureException {
45 | // no op, Maven doesn't even call this method but I have to implement it because it's on the interface.
46 | }
47 |
48 | public void generate(Sink sink, Locale locale) throws MavenReportException {
49 | try {
50 | runScalaTest(configuration());
51 | }
52 | catch (MojoFailureException e) {
53 | throw new MavenReportException("Failure executing ScalaTest", e);
54 | }
55 | catch (RuntimeException e) {
56 | throw new MavenReportException("Failure executing ScalaTest", e);
57 | }
58 |
59 | // ScalaTest outputs plain text but the Mojo requires HTML output so we'll create a bare-bones HTML doc and
60 | // embed the ScalaTest output inside a
in that doc.
61 |
62 | try {
63 | sink.head();
64 | sink.title();
65 | sink.text(getLocalizedString(locale, "reporter.mojo.outputTitle"));
66 | sink.title_();
67 | sink.head_();
68 |
69 | sink.body();
70 | sink.sectionTitle1();
71 | sink.text(getLocalizedString(locale, "reporter.mojo.outputTitle"));
72 | sink.sectionTitle1_();
73 | sink.verbatim(false);
74 | sink.text(getScalaTestOutputFromFile());
75 | sink.verbatim_();
76 | sink.body_();
77 |
78 | sink.flush();
79 | sink.close();
80 | }
81 | catch (IOException ioe) {
82 | throw new MavenReportException("Failure generating ScalaTest report", ioe);
83 | }
84 | }
85 |
86 | private String[] configuration() {
87 | return concat(
88 | sharedConfiguration(),
89 | fileReporterConfig()
90 | );
91 | }
92 |
93 | private List fileReporterConfig() {
94 | File tmpDir = new File(System.getProperty("java.io.tmpdir"));
95 | if (fileReporterOptions != null) {
96 | return reporterArg("-f", fileReporterOptions + " tempScalaTestOutput.txt", fileRelativeTo(tmpDir));
97 | }
98 | return reporterArg("-f", "tempScalaTestOutput.txt", fileRelativeTo(tmpDir));
99 | }
100 |
101 | private String getScalaTestOutputFromFile() throws IOException {
102 | StringWriter fileContents = new StringWriter(1024);
103 | PrintWriter writer = new PrintWriter(fileContents, true);
104 |
105 | // ScalaTest's FileReporter uses default character encoding so that's what we'll use here, too.
106 | File outputFile = new File(System.getProperty("java.io.tmpdir"), "tempScalaTestOutput.txt");
107 | BufferedReader reader = new BufferedReader(new FileReader(outputFile));
108 |
109 | try {
110 | String line = null;
111 | while ((line = reader.readLine()) != null) {
112 | writer.println(line);
113 | }
114 | return fileContents.toString();
115 | }
116 | finally {
117 | try {
118 | reader.close();
119 | outputFile.delete();
120 | }
121 | catch (IOException ignored) {}
122 | }
123 | }
124 |
125 | public String getOutputName() {
126 | return "scalatest-output";
127 | }
128 |
129 | public String getCategoryName() {
130 | return CATEGORY_PROJECT_REPORTS;
131 | }
132 |
133 | public String getName(Locale locale) {
134 | return getLocalizedString(locale, "reporter.mojo.name");
135 | }
136 |
137 | public String getDescription(Locale locale) {
138 | return getLocalizedString(locale, "reporter.mojo.description");
139 | }
140 |
141 | public void setReportOutputDirectory(File outputDirectory) {
142 | reportingOutputDirectory = outputDirectory;
143 | }
144 |
145 | public File getReportOutputDirectory() {
146 | return reportingOutputDirectory;
147 | }
148 |
149 | public boolean isExternalReport() {
150 | return false;
151 | }
152 |
153 | public boolean canGenerateReport() {
154 | return true;
155 | }
156 |
157 | private String getLocalizedString(Locale locale, String resourceKey) {
158 | return ResourceBundle.getBundle("mojoResources", locale, getClass().getClassLoader()).getString(resourceKey);
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/src/site/apt/usage.apt:
--------------------------------------------------------------------------------
1 | Usage Documentation
2 |
3 | Replacing the use of Maven's Surefire plugin with this plugin involves two simple steps:
4 |
5 | [[1]] Disable the maven-surefire-plugin
6 |
7 | [[2]] Configure this scalatest-maven-plugin to run in the test phase
8 |
9 | []
10 |
11 | To replace the maven-surefire-report-plugin with this plugin, simply add the scalatest-maven-plugin instead of
12 | the maven-surefire-report-plugin to the reporting section of the POM.
13 |
14 | For example, in the build section of the POM, add something like the following:
15 |
16 | +--
17 |
18 | ...
19 |
20 |
21 |
22 | org.apache.maven.plugins
23 | maven-surefire-plugin
24 |
25 | true
26 |
27 |
28 |
29 |
30 |
31 | org.scalatest
32 | scalatest-maven-plugin
33 |
34 |
35 |
36 | test
37 |
38 |
39 | ...
40 |
41 |
42 |
43 |
44 |
45 | ...
46 |
47 | +--
48 |
49 | The important item in the above POM definition is the \ configuration specified
50 | on the maven-surefire-plugin. There is nothing inherently wrong in having Surefire run your
51 | tests, but if you want all your tests to run through ScalaTest instead, then disabling Surefire
52 | in this manner will keep your tests from running twice. Since the "test" goal of the
53 | scalatest-maven-plugin is tied to the test phase by default you do not need to do this explicitly
54 | in your POM.
55 |
56 | Optionally, in the reporting section of the POM you can add something like the following:
57 |
58 | +--
59 |
60 | ...
61 |
62 |
63 | org.scalatest
64 | scalatest-maven-plugin
65 |
66 | ...
67 |
68 |
69 |
70 | ...
71 |
72 | +--
73 |
74 | Adding this configuration will tell the plugin's "reporter" goal to execute during
75 | site generation. This goal will run the tests and output the results to an HTML file that
76 | will link into the project's site. ScalaTest does not yet have an HTML Reporter implementation,
77 | so this goal will instead embed the plain text output of ScalaTest's FileReporter into a \
78 | element in a wrapper HTML page with seamless incorporation into Maven's site infrastructure.
79 |
80 | <> Adding the reporter goal will, like the default behavior of maven-surefire-report-plugin, cause
81 | your tests to run twice: once in the test phase and again in the site phase. Unlike the maven-surefire-report-plugin,
82 | the reporter goal of this plugin have a "report-only" configuration option to incorporate the test
83 | results of the prior run.
84 |
85 | * Customizing ScalaTest
86 |
87 | While using this plugin does allow Maven to execute your tests without the need to
88 | use the "@RunWith(classOf[JUnitRunner])" boilerplate at the top of every test class, which is certainly
89 | an advantage, the main benefit of this plugin is that it allows you to leverage the customization
90 | options of ScalaTest's Runner class as part of the standard Maven build. It is true that through either
91 | the exec-maven-plugin or the "run" goal of the maven-scala-plugin you could launch the Runner class
92 | with command-line arguments tailored to your preferences, but that approach is not integrated with the
93 | Maven lifecycle; it's a manual step, test failures will not fail your build, and the test results
94 | will not be included in your project site. This plugin addresses those problems.
95 |
96 | The configuration options available to each of the goals are included in the goal documentation, but generally
97 | speaking, all or at least most of the command line arguments available on the Runner class are exposed
98 | as options on the "test" and "gui" goals. Creative use of these configuration options can allow you to
99 | run your "fast" tests in one Maven profile and your "slow" tests in a different Maven profile or to filter
100 | certain suites from the execution.
101 |
102 | For example, let's say that you have some tests tagged with a "Slow" Tag and others tagged with a "Fast" Tag.
103 | All the goals in this plugin support the "includes" and "excludes" configuration options, which map to the -n
104 | and -l Runner command-line arguments, respectively. Now let's also say that you want to run your "Fast" tests
105 | on every SCM commit while only running the "Slow" tests manually or from a nightly build. Using Maven profiles,
106 | you can configure your POM similar to the following to accomplish this objective:
107 |
108 | +--
109 |
110 | ...
111 |
112 | org.scalatest
113 | scalatest-maven-plugin
114 |
115 |
116 |
117 | test
118 |
119 |
120 | test-results
121 | Slow
122 |
123 |
124 |
125 |
126 | ...
127 |
128 | ...
129 |
130 |
131 | nightly-build
132 |
133 |
134 |
135 | org.scalatest
136 | scalatest-maven-plugin
137 |
138 |
139 |
140 | test
141 |
142 |
143 | test-results
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 | +--
154 |
155 | The "test" and "gui" goals support multiple Reporters of different types and formatting options for each
156 | of those Reporters. For example, given this elaborate Reporter configuration
157 |
158 | +--
159 | NCXEHLOWFD test-results/text/constrained.txt,test-results/text/full.txt
160 | test-results/xml
161 | +--
162 |
163 | the test results will be written to three locations:
164 |
165 | [[1]] the constrained.txt file in the test-results/text directory with the NCXEHLOWFD Reporting options applied
166 |
167 | [[2]] the full.txt file in the test-results/text directory with no Reporting options applied
168 |
169 | [[3]] the test-results/xml directory as a set of files that adhere to the JUnit XML format
170 |
171 | []
172 |
173 | The "reporter" goal does not support this level of customization, however, because it doesn't really apply.
174 | Instead, this goal has a "fileReporterOptions" option that allows you to specify the formatting options to
175 | pass to the FileReporter. The plugin itself controls the name of that file, and the location of the file
176 | is configured through the standard "reportOutputDirectory" as is convention in report goals of other plugins.
177 |
--------------------------------------------------------------------------------
/src/main/java/org/scalatest/tools/maven/TestMojo.java:
--------------------------------------------------------------------------------
1 | package org.scalatest.tools.maven;
2 |
3 | import org.apache.maven.plugin.MojoFailureException;
4 |
5 | import java.io.File;
6 | import java.io.FileWriter;
7 | import java.io.IOException;
8 | import java.io.Writer;
9 | import java.util.ArrayList;
10 | import java.util.Collections;
11 | import java.util.List;
12 |
13 | import static java.util.Collections.singletonList;
14 | import static java.util.Collections.unmodifiableList;
15 | import static org.scalatest.tools.maven.MojoUtils.*;
16 |
17 | /**
18 | * Provides a bridge between Maven and the command-line form of ScalaTest's Runner.
19 | * Many of the configuration options available on this goal
20 | * are directly reflected in the Runner ScalaDoc on http://www.scalatest.org.
21 | *
22 | * @author Sean Griffin
23 | * @author Mike Pilquist
24 | * @author Jon-Anders Teigen
25 | * @author Bill Venners
26 | * @phase test
27 | * @goal test
28 | * @threadSafe
29 | */
30 | public class TestMojo extends AbstractScalaTestMojo {
31 |
32 | /**
33 | * Output directory in which ScalaTest file reports should be written to. Passed to ScalaTest via the -f argument.
34 | * @parameter default-value="${project.build.directory}/scalatest-reports" property="scalatest.reportsDirectory"
35 | * @required
36 | */
37 | File reportsDirectory;
38 |
39 | /**
40 | * Set to true to skip execution of tests.
41 | * @parameter property="skipTests"
42 | */
43 | boolean skipTests;
44 |
45 | /**
46 | * Set to true to avoid failing the build when tests fail
47 | * @parameter property="maven.test.failure.ignore"
48 | */
49 | boolean testFailureIgnore;
50 |
51 | /**
52 | * Set to true to avoid failing the build when ScalaTest is not available on classpath
53 | * @parameter property="noScalaTestIgnore"
54 | */
55 | boolean noScalaTestIgnore;
56 |
57 | /**
58 | * Comma separated list of filereporters. A filereporter consists of an optional
59 | * configuration and a mandatory filename, separated by a whitespace. E.g all.txt,XE ignored_and_pending.txt
60 | * For more info on configuring reporters, see the scalatest documentation.
61 | * @parameter property="filereports"
62 | */
63 | String filereports;
64 |
65 | /**
66 | * Comma separated list of htmlreporters. An htmlreporter
67 | * consists of a mandatory directory and an optional css file
68 | * name, separated by whitespace. E.g:
69 | *
70 | * <htmlreporters>
71 | * target/htmldir,
72 | * target/myhtmldir src/my.css
73 | * </htmlreporters>
74 | *
75 | * For more info on configuring reporters, see the scalatest documentation.
76 | * @parameter property="htmlreporters"
77 | */
78 | String htmlreporters;
79 |
80 | /**
81 | * Comma separated list of reporters. A reporter consist of an optional configuration
82 | * and a mandatory reporter classname, separated by whitespace. The reporter classname
83 | * must be the fully qualified name of a class extending org.scalatest.Reporter
84 | * E.g C my.SuccessReporter,my.EverythingReporter
85 | * For more info on configuring reporters, see the ScalaTest documentation.
86 | * @parameter property="reporters"
87 | */
88 | String reporters;
89 |
90 | /**
91 | * Comma separated list of junitxml. A junitxml consists of an optional configuration
92 | * and a mandatory directory for the xml files, separated by whitespace.
93 | * For more info on configuring reporters, see the scalatest documentation.
94 | * @parameter property="junitxml"
95 | */
96 | String junitxml;
97 |
98 | /**
99 | * Configuration for logging to stdout. (This logger is always enabled)
100 | * For more info on configuring reporters, see the scalatest documentation.
101 | * @parameter property="stdout"
102 | */
103 | String stdout;
104 |
105 | /**
106 | * Configuration for logging to stderr. It is disabled by default, but will be enabled
107 | * when configured. Empty configuration just means enable.
108 | * For more info on configuring reporters, see the scalatest documentation.
109 | * @parameter property="stderr"
110 | */
111 | String stderr;
112 |
113 | /**
114 | * Set this to "true" to redirect the unit test standard output to a file
115 | * (found in reportsDirectory/scalatest-output.txt by default).
116 | *
117 | * NOTE: Works only if forkMode="once"
118 | *
119 | * @parameter property="maven.test.redirectTestOutputToFile" default-value="false"
120 | */
121 | boolean redirectTestOutputToFile;
122 |
123 | /**
124 | * Name of the file where the test output is redirected if enabled
125 | *
126 | * @parameter default-value="scalatest-output.txt"
127 | */
128 | String testOutputFileName;
129 |
130 | public void execute() throws MojoFailureException {
131 | getLog().info("ScalaTest report directory: " + reportsDirectory);
132 |
133 | if (skipTests) {
134 | getLog().info("Tests are skipped.");
135 | }
136 | else if (noScalaTestIgnore) {
137 | if (isScalaTestAvailable()) {
138 | if (!runScalaTest(configuration()) && !testFailureIgnore) {
139 | throw new MojoFailureException("There are test failures");
140 | }
141 | }
142 | else
143 | getLog().info("Skipped because ScalaTest not available on classpath.");
144 | }
145 | else {
146 | if (!runScalaTest(configuration()) && !testFailureIgnore) {
147 | if (isScalaTestAvailable())
148 | throw new MojoFailureException("There are test failures");
149 | else
150 | throw new MojoFailureException("Failure because ScalaTest not available on classpath.");
151 | }
152 | }
153 | }
154 |
155 | String[] configuration() {
156 | return concat(
157 | sharedConfiguration(),
158 | stdout(),
159 | stderr(),
160 | filereports(),
161 | htmlreporters(),
162 | reporters(),
163 | junitxml()
164 | );
165 | }
166 |
167 | // These private methods create the relevant portion of the command line
168 | // to pass to Runner based on the corresponding Maven configuration parameter.
169 | private List stdout() {
170 | final String stdoutProcessed = maybeRemoveAnsiCodes(stdout);
171 | return unmodifiableList(singletonList(stdoutProcessed == null ? "-o" : "-o" + stdoutProcessed));
172 | }
173 |
174 | private List stderr() {
175 | return stderr == null ? Collections.emptyList() : unmodifiableList(singletonList("-e" + stderr));
176 | }
177 |
178 | private String maybeRemoveAnsiCodes(String streamParams) {
179 | return redirectTestOutputToFile
180 | ? maybeAppendLetter(streamParams, "W")
181 | : streamParams;
182 | }
183 |
184 | private String maybeAppendLetter(String string, String letter) {
185 | if (string == null) {
186 | return letter;
187 | }
188 |
189 | if (string.contains(letter)) {
190 | return string;
191 | }
192 |
193 | return string + letter;
194 | }
195 |
196 | private List filereports() {
197 | return unmodifiableList(reporterArg("-f", filereports, fileRelativeTo(reportsDirectory)));
198 | }
199 |
200 | private List htmlreporters() {
201 | List r = new ArrayList();
202 |
203 | for (String arg : splitOnComma(htmlreporters)) {
204 | String[] split = arg.split("\\s+");
205 |
206 | if (split.length > 0) {
207 | r.add("-h");
208 | r.add(split[0]);
209 |
210 | if (split.length > 1) {
211 | r.add("-Y");
212 | r.add(split[1]);
213 | }
214 | }
215 | }
216 | return unmodifiableList(r);
217 | }
218 |
219 | private List reporters() {
220 | return reporterArg("-C", reporters, passThrough);
221 | }
222 |
223 | private List junitxml(){
224 | return reporterArg("-u", junitxml, dirRelativeTo(reportsDirectory));
225 | }
226 |
227 | protected Writer getOutputWriter() throws MojoFailureException {
228 | return redirectTestOutputToFile ? newFileWriter() : super.getOutputWriter();
229 | }
230 |
231 | private Writer newFileWriter() throws MojoFailureException {
232 | final File outputFile = new File(reportsDirectory, testOutputFileName);
233 |
234 | if (!reportsDirectory.exists() && !reportsDirectory.mkdirs()) {
235 | throw new MojoFailureException("Unable to create directory path: " + reportsDirectory);
236 | }
237 |
238 | try {
239 | return new FileWriter(outputFile);
240 | } catch (IOException e) {
241 | throw new MojoFailureException("Unable to access the output file: '" + outputFile + "'", e);
242 | }
243 | }
244 | }
245 |
--------------------------------------------------------------------------------
/src/test/scala/org/scalatest/tools/maven/PluginTest.scala:
--------------------------------------------------------------------------------
1 | package org.scalatest.tools.maven
2 |
3 | import java.io.File
4 | import org.scalatest.matchers.should.Matchers
5 | import org.scalatestplus.junit.JUnit3Suite
6 | import java.util.ArrayList
7 | import org.scalatest.BeforeAndAfterAll
8 |
9 | /**
10 | * @author Jon -Anders Teigen
11 | */
12 | final class PluginTest
13 | extends JUnit3Suite
14 | with BeforeAndAfterAll
15 | with Matchers
16 | with PluginMatchers {
17 | val tmpDir = new File(System.getProperty("java.io.tmpdir"))
18 | val reportsDirectory = new File(tmpDir, "reportsDirectory")
19 | val baseDir = new File(tmpDir, "basedir");
20 | val testOutputDirectory = {
21 | val dir = new File(reportsDirectory, "testOutputDirectory")
22 | dir.mkdirs()
23 | dir.getAbsolutePath
24 | }
25 | val outputDirectory = {
26 | val dir = new File(reportsDirectory, "outputDirectory")
27 | dir.mkdirs()
28 | dir.getAbsolutePath
29 | }
30 |
31 | override def afterAll() {
32 | def delete(it: File) {
33 | if (it.isFile) {
34 | it.delete()
35 | } else {
36 | for (d <- it.listFiles)
37 | delete(d)
38 | }
39 | }
40 | delete(reportsDirectory)
41 | delete(baseDir);
42 | }
43 |
44 | def jlist(a: String*) = new ArrayList[String]() {for (e <- a) this.add(e)}
45 |
46 | def comma(a: String*) = a mkString ","
47 |
48 | def configure(m: TestMojo => Unit) = {
49 | val mojo = new TestMojo
50 | mojo.reportsDirectory = reportsDirectory
51 | mojo.testOutputDirectory = new File(testOutputDirectory)
52 | mojo.outputDirectory = new File(outputDirectory)
53 | m(mojo)
54 | mojo.configuration
55 | }
56 |
57 | def testDefault {
58 | val config = configure(_ => ())
59 | config should contain("-o")
60 | config should containCompoundArgs("-R", outputDirectory, testOutputDirectory)
61 | config should have length (3)
62 | }
63 |
64 | def testConfigs {
65 | val config = configure(_.config = comma("foo=bar", "monkey=donkey"))
66 | config should contain("-Dfoo=bar")
67 | config should contain("-Dmonkey=donkey")
68 | }
69 |
70 | def testRunpath {
71 | configure(_.runpath = comma("http://foo.com/my.jar", "/some/where")) should containCompoundArgs("-R", outputDirectory, testOutputDirectory, "http://foo.com/my.jar", "/some/where")
72 | }
73 |
74 | def testFilereporters {
75 | val config = configure(_.filereports = comma("foo.txt", "YZT some.txt"))
76 | config should containSlice("-f", new File(reportsDirectory, "foo.txt").getAbsolutePath)
77 | config should containSlice("-fYZT", new File(reportsDirectory, "some.txt").getAbsolutePath)
78 | }
79 |
80 | def testHtmlreporters {
81 | val config = configure(_.htmlreporters =
82 | comma("target/htmldir", "target/myhtmldir src/resources/my.css"))
83 |
84 | config should containSlice("-h", "target/htmldir")
85 | config should containSlice("-h", "target/myhtmldir",
86 | "-Y", "src/resources/my.css")
87 | }
88 |
89 | def testReporters {
90 | val config = configure(_.reporters = comma("YZT org.my.reporter", "org.your.reporter"))
91 | config should containSlice("-CYZT", "org.my.reporter")
92 | config should containSlice("-C", "org.your.reporter")
93 | }
94 |
95 | def testJUnitXmlReporters {
96 | val config = configure(_.junitxml = comma("some/foo.xml", "XYZ other.xml"))
97 | config should containSlice("-u", new File(reportsDirectory, "some/foo.xml").getAbsolutePath)
98 | config should containSlice("-uXYZ", new File(reportsDirectory, "other.xml").getAbsolutePath)
99 | }
100 |
101 | def testStdOutReporter {
102 | configure(_.stdout = "GUP") should contain("-oGUP")
103 | }
104 |
105 | def testStdOutReporterWithRedirect {
106 | configure(_.redirectTestOutputToFile = true) should contain("-oW")
107 | }
108 |
109 | def testStdErrReporter {
110 | configure(_.stderr = "BIS") should contain("-eBIS")
111 | }
112 |
113 | def testIncludes {
114 | configure(_.tagsToInclude = comma("a", "b", "c")) should containCompoundArgs("-n", "a", "b", "c")
115 | }
116 |
117 | def testExcludes {
118 | configure(_.tagsToExclude = comma("a", "b", "c")) should containCompoundArgs("-l", "a", "b", "c")
119 | }
120 |
121 | def testConcurrent {
122 | configure(_.parallel = true) should contain("-P")
123 | configure(_.parallel = false) should not contain ("-P")
124 | }
125 |
126 | def testConcurrentSuiteSorting {
127 | configure { m =>
128 | m.parallel = true
129 | m.suiteSorting = true
130 | } should containSlice("-PS")
131 | }
132 |
133 | def testConcurrentThreadCount {
134 | configure { m =>
135 | m.parallel = true
136 | m.threadCount = 4
137 | } should containSlice("-P4")
138 | }
139 |
140 | def testConcurrentSuiteSortingThreadCount {
141 | configure { m =>
142 | m.parallel = true
143 | m.suiteSorting = true
144 | m.threadCount = 4
145 | } should containSlice("-PS4")
146 | }
147 |
148 | def testSuiteSorting {
149 | // Suite sorting count should have no effect if parallel is not set to true.
150 | configure { m =>
151 | m.suiteSorting = true
152 | } shouldNot containSlice ("-PS")
153 | }
154 |
155 | def testSuiteSortingThreadCount {
156 | // Suite sorting and thread count should have no effect if parallel is not set to true.
157 | configure { m =>
158 | m.suiteSorting = true
159 | m.threadCount = 4
160 | } shouldNot containSlice ("-PS4")
161 | }
162 |
163 | def testThreadCount {
164 | // Thread count should have no effect if parallel is not set to true.
165 | configure { m =>
166 | m.threadCount = 4
167 | } shouldNot containSlice ("-P4")
168 | }
169 |
170 | def testSuites {
171 | val suites: String = comma(" a ",
172 | "b",
173 | "foo @bar baz",
174 | " zowie\n zip zap ")
175 |
176 | val config = configure(_.suites = suites)
177 |
178 | config should containSlice ("-s", "a")
179 | config should containSlice ("-s", "b")
180 | config should containSlice ("-s", "foo", "-t", "bar baz")
181 | config should containSlice ("-s", "zowie", "-z", "zip zap")
182 | }
183 |
184 | def testSuitesAndTests {
185 | val suites: String = comma(" a ", "b c")
186 | val tests: String = comma(" d ", "@e")
187 |
188 | val config = configure(x => {x.suites = suites; x.tests = tests})
189 |
190 | config should containSlice ("-z", "d",
191 | "-t", "e",
192 | "-s", "a",
193 | "-s", "b", "-z", "c")
194 | }
195 |
196 | def testTests {
197 | val tests: String= comma(" @a ", " b ", "@c")
198 |
199 | val config = configure(_.tests = tests)
200 |
201 | config should containSlice("-t", "a")
202 | config should containSlice("-z", "b")
203 | config should containSlice("-t", "c")
204 | }
205 |
206 | //
207 | // Verify that a comma can be escaped with a backslash in order to
208 | // support a test name that contains a comma.
209 | //
210 | def testTestsWithCommas {
211 | configure(_.tests = comma("a\\, bc", "b", "c")) should containSuiteArgs("-z", "a, bc", "b", "c")
212 | }
213 |
214 | def testSuffixes {
215 | configure(_.suffixes = "(?
2 |
4 |
5 | 4.0.0
6 |
7 |
8 | org.sonatype.oss
9 | oss-parent
10 | 9
11 |
12 |
13 | org.scalatest
14 | scalatest-maven-plugin
15 | maven-plugin
16 | 2.2.0
17 | ScalaTest Maven Plugin
18 | Integrates ScalaTest into Maven
19 | https://www.scalatest.org/user_guide/using_the_scalatest_maven_plugin
20 |
21 |
22 | 2.12
23 | 17
24 | ${scala.major.version}.${scala.minor.version}
25 | 3.2.14
26 | 4.3.0
27 |
28 | UTF-8
29 | UTF-8
30 |
31 | ${maven.build.timestamp}
32 | yyyy-MM-dd
33 | 1.6
34 | 2.9
35 | 3.8.6
36 | 3.8.6
37 | 4.12
38 | 3.1.1
39 | 1.4.1
40 |
41 | 8
42 |
43 |
44 |
45 |
46 | the Apache License, ASL Version 2.0
47 | http://www.apache.org/licenses/LICENSE-2.0
48 | repo
49 |
50 |
51 |
52 |
53 | https://github.com/scalatest/scalatest-maven-plugin
54 | scm:git:git@github.com:scalatest/scalatest-maven-plugin.git
55 | scm:git:git@github.com:scalatest/scalatest-maven-plugin.git
56 |
57 |
58 |
59 |
60 | teigen
61 | Jon-Anders Teigen
62 |
63 |
64 | trenchguinea
65 | Sean Griffin
66 | trenchguinea@gmail.com
67 |
68 |
69 | gcberger
70 | George Berger
71 |
72 |
73 | cheeseng
74 | Chua Chee Seng
75 |
76 |
77 | bvenners
78 | Bill Venners
79 |
80 |
81 |
82 |
83 |
84 | org.apache.maven
85 | maven-plugin-api
86 | ${maven.plugin.api.version}
87 |
88 |
89 | org.apache.maven
90 | maven-core
91 | ${maven.core.version}
92 |
93 |
94 | org.apache.maven.reporting
95 | maven-reporting-api
96 | ${maven.reporting.api.version}
97 |
98 |
99 | org.scalatest
100 | scalatest-core_${scala.major.version}
101 | ${scalatest.version}
102 | test
103 |
104 |
105 | org.scalatest
106 | scalatest-shouldmatchers_${scala.major.version}
107 | ${scalatest.version}
108 | test
109 |
110 |
111 | org.scalatestplus
112 | junit-4-13_${scala.major.version}
113 | ${scalatest.version}.0
114 | test
115 |
116 |
117 | org.scala-lang
118 | scala-library
119 | ${scala.major.version}.${scala.minor.version}
120 | test
121 |
122 |
123 | com.github.stefanbirkner
124 | system-rules
125 | 1.19.0
126 | test
127 |
128 |
129 |
130 |
131 | src/main/java
132 | src/test/scala
133 |
134 |
135 |
136 |
137 | maven-site-plugin
138 | 3.5.1
139 |
140 |
141 |
142 |
143 |
144 |
145 | net.alchim31.maven
146 | scala-maven-plugin
147 | ${scala.maven.plugin.version}
148 |
149 | incremental
150 | ${scala.version}
151 |
152 |
153 |
154 | scala-test-compile
155 | test-compile
156 |
157 | testCompile
158 |
159 |
160 |
161 |
162 |
163 | maven-compiler-plugin
164 | 3.8.1
165 |
166 | ${java.version}
167 | ${java.version}
168 |
169 |
170 |
171 | org.apache.maven.plugins
172 | maven-surefire-plugin
173 | 2.5
174 |
175 | -enableassertions
176 |
177 |
178 |
179 | maven-invoker-plugin
180 | 1.3
181 |
182 |
183 | integration-test
184 | integration-test
185 |
186 | install
187 | run
188 |
189 |
190 |
191 |
192 | false
193 |
194 | src/it
195 | ${project.build.directory}/it
196 |
197 | lift/pom.xml
198 | spaces in path/pom.xml
199 | redirect-output/pom.xml
200 | no-scalatest-ignore/pom.xml
201 |
202 | ${project.build.directory}/local-repo
203 | true
204 | src/it/settings.xml
205 | verify
206 |
207 | clean
208 | test
209 | site
210 |
211 |
212 |
213 |
214 | org.apache.maven.plugins
215 | maven-enforcer-plugin
216 | ${maven.enforcer.plugin.version}
217 |
218 |
219 | enforce-maven
220 |
221 | enforce
222 |
223 |
224 |
225 |
226 |
227 | [3.0.4,)
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 | release
240 |
241 |
242 |
243 | org.apache.maven.plugins
244 | maven-javadoc-plugin
245 | 2.7
246 |
247 | -Xdoclint:none
248 |
249 |
250 |
251 | org.apache.maven.plugins
252 | maven-gpg-plugin
253 | ${maven.gpg.plugin.version}
254 |
255 |
256 | sign-artifacts
257 | verify
258 |
259 | sign
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 | org.apache.maven.plugins
270 | maven-plugin-plugin
271 | 2.6
272 |
273 |
274 | org.codehaus.mojo
275 | cobertura-maven-plugin
276 | 2.5.1
277 |
278 |
279 | org.apache.maven.plugins
280 | maven-changes-plugin
281 | 2.6
282 |
283 | true
284 |
285 |
286 |
287 | org.apache.maven.plugins
288 | maven-dependency-plugin
289 | 2.3
290 |
291 |
292 | org.apache.maven.plugins
293 | maven-javadoc-plugin
294 | 2.7
295 |
296 | UTF-8
297 | UTF-8
298 | true
299 | package
300 | ${java.version}
301 | true
302 |
303 |
304 |
305 | org.apache.maven.plugins
306 | maven-jxr-plugin
307 | 2.2
308 |
309 | UTF-8
310 | UTF-8
311 |
312 |
313 |
314 | org.apache.maven.plugins
315 | maven-surefire-report-plugin
316 | 2.5
317 |
318 |
319 |
320 |
321 |
322 |
323 |
--------------------------------------------------------------------------------
/src/main/java/org/scalatest/tools/maven/AbstractScalaTestMojo.java:
--------------------------------------------------------------------------------
1 | package org.scalatest.tools.maven;
2 |
3 | import org.apache.maven.artifact.DependencyResolutionRequiredException;
4 | import org.apache.maven.plugin.AbstractMojo;
5 | import org.apache.maven.plugin.MojoFailureException;
6 | import org.apache.maven.project.MavenProject;
7 | import org.codehaus.plexus.util.cli.*;
8 |
9 | import java.io.File;
10 | import java.io.IOException;
11 | import java.io.PrintWriter;
12 | import java.io.Writer;
13 | import java.lang.reflect.InvocationTargetException;
14 | import java.lang.reflect.Method;
15 | import java.net.MalformedURLException;
16 | import java.net.URL;
17 | import java.net.URLClassLoader;
18 | import java.util.*;
19 |
20 | import static java.util.Collections.singletonList;
21 | import static java.util.Collections.unmodifiableList;
22 | import static org.scalatest.tools.maven.MojoUtils.*;
23 |
24 | /**
25 | * Provides the base for all mojos.
26 | *
27 | * @author Jon-Anders Teigen
28 | * @author Sean Griffin
29 | * @author Mike Pilquist
30 | * @author Bill Venners
31 | *
32 | * @requiresDependencyResolution test
33 | */
34 | abstract class AbstractScalaTestMojo extends AbstractMojo {
35 | /**
36 | * Injected by Maven so that forked process can be
37 | * launched from the working directory of current maven project in a multi-module build. Should not be user facing.
38 | * @parameter default-value="${project}"
39 | * @required
40 | * @readonly
41 | */
42 | MavenProject project;
43 |
44 | /**
45 | * Injected by Maven so that it can be included in the run path. Should not be user facing.
46 | * @parameter property="project.build.testOutputDirectory"
47 | * @required
48 | * @readOnly
49 | */
50 | File testOutputDirectory;
51 |
52 | /**
53 | * Injected by Maven so that it can be included in the run path. Should not be user facing.
54 | * @parameter property="project.build.outputDirectory"
55 | * @required
56 | * @readOnly
57 | */
58 | File outputDirectory;
59 |
60 | /**
61 | * Comma separated list of additional elements to be added
62 | * to the ScalaTest runpath. ${project.build.outputDirectory} and
63 | * ${project.build.testOutputDirectory} are included by default
64 | * @parameter property="runpath"
65 | */
66 | String runpath;
67 |
68 | /**
69 | * Comma separated list of suites to be executed.
70 | * @parameter property="suites"
71 | */
72 | String suites;
73 |
74 | /**
75 | * Comma separated list of tests to be executed
76 | * @parameter property="tests"
77 | */
78 | String tests;
79 |
80 | /**
81 | * Regex of suffixes to filter discovered suites
82 | * @parameter property="suffixes"
83 | */
84 | String suffixes;
85 |
86 | /**
87 | * Comma separated list of tags to include
88 | * @parameter property="tagsToInclude"
89 | */
90 | String tagsToInclude;
91 |
92 | /**
93 | * Comma separated list of tags to exclude
94 | * @parameter property="tagsToExclude"
95 | */
96 | String tagsToExclude;
97 |
98 | /**
99 | * Comma separated list of configuration parameters to pass to ScalaTest.
100 | * The parameters must be of the format <key>=<value>. E.g foo=bar,monkey=donkey
101 | * @parameter property="config"
102 | */
103 | String config;
104 |
105 | /**
106 | * Set to true to run suites concurrently
107 | * @parameter property="parallel"
108 | */
109 | boolean parallel;
110 |
111 | /**
112 | * Set to true to enable suite sorting, this only takes effect if parallel is set to true.
113 | * @parameter property="suiteSorting"
114 | */
115 | boolean suiteSorting;
116 |
117 | /**
118 | * Set the thread count for parallel tests execution, this only takes effect if parallel is set to true.
119 | * @parameter property="threadCount"
120 | */
121 | int threadCount;
122 |
123 | /**
124 | * Comma separated list of packages containing suites to execute
125 | * @parameter property="membersOnlySuites"
126 | */
127 | String membersOnlySuites;
128 |
129 | // TODO: Change this to wildcard and membersOnly
130 | /**
131 | * Comma separated list of wildcard suite names to execute
132 | * @parameter property="wildcardSuites"
133 | */
134 | String wildcardSuites;
135 |
136 | /**
137 | * Comma separated list of testNG xml files to execute
138 | * @parameter property="testNGXMLFiles"
139 | */
140 | String testNGConfigFiles;
141 |
142 | /**
143 | * Comma separated list of files to store names of failed and
144 | * canceled tests into.
145 | * @parameter property="memoryFiles"
146 | */
147 | String memoryFiles;
148 |
149 | /**
150 | * Comma separated list of files to store names of failed and
151 | * canceled tests into.
152 | * @parameter property="testsFiles"
153 | */
154 | String testsFiles;
155 |
156 | /**
157 | * Comma separated list of JUnit suites/tests to execute
158 | * @parameter property="junitClasses"
159 | */
160 | String jUnitClasses;
161 |
162 | /**
163 | * Option to specify the forking mode. Can be "never" or "once". "always", which would
164 | * fork for each test-class, may be supported later.
165 | *
166 | * @parameter property="forkMode" default-value="once"
167 | */
168 | String forkMode;
169 |
170 | /**
171 | * Option to specify additional JVM options to pass to the forked process.
172 | *
173 | * @parameter property="argLine"
174 | */
175 | String argLine;
176 |
177 | /**
178 | * Additional environment variables to pass to the forked process.
179 | *
180 | * @parameter
181 | */
182 | Map environmentVariables;
183 |
184 | /**
185 | * Additional system properties to pass to the forked process.
186 | *
187 | * @parameter
188 | */
189 | Map systemProperties;
190 |
191 | /**
192 | * Option to specify whether the forked process should wait at startup for a remote debugger to attach.
193 | *
194 | *
If set to true, the forked process will suspend at startup and wait for a remote
195 | * debugger to attach to the configured port.
196 | *
197 | * @parameter property="debugForkedProcess" default-value="false"
198 | */
199 | boolean debugForkedProcess;
200 |
201 | /**
202 | * JVM options to pass to the forked process when debugForkedProcess is true.
203 | *
204 | *
If set to a non-empty value, the standard debug arguments are replaced by the specified arguments.
205 | * This allows customization of how remote debugging is done, without having to reconfigure the JVM
206 | * options in argLine.
207 | *
208 | * @parameter property="debugArgLine"
209 | */
210 | String debugArgLine;
211 |
212 | /**
213 | * Port to listen on when debugging the forked process.
214 | *
215 | * @parameter property="debuggerPort" default-value="5005"
216 | */
217 | int debuggerPort = 5005;
218 |
219 | /**
220 | * Timeout in seconds to allow the forked process to run before killing it and failing the test run.
221 | *
222 | *
If set to 0, process never times out.
223 | *
224 | * @parameter property="timeout" default-value="0"
225 | */
226 | int forkedProcessTimeoutInSeconds = 0;
227 |
228 | /**
229 | * Whether or not to log the command used to launch the forked process.
230 | *
231 | * @parameter property="logForkedProcessCommand" default-value="false"
232 | */
233 | boolean logForkedProcessCommand;
234 |
235 | /**
236 | * Span scale factor.
237 | *
238 | * @parameter property="spanScaleFactor" default-value="1.0"
239 | */
240 | double spanScaleFactor = 1.0;
241 |
242 | /**
243 | * The current working directory for forked process. Optional. If not specified, basedir will be used.
244 | *
245 | * @parameter property="workingDirectory"
246 | */
247 | String workingDirectory;
248 |
249 | /**
250 | * Option to specify an alternative path to JVM (or path to the java executable) to use with
251 | * the forked process.
252 | *
253 | * @parameter property="jvm"
254 | */
255 | String jvm;
256 |
257 | // runScalaTest is called by the concrete mojo subclasses TODO: make it protected and others too
258 | // Returns true if all tests pass
259 | boolean runScalaTest(String[] args) throws MojoFailureException {
260 | getLog().debug(Arrays.toString(args));
261 | if (forkMode.equals("never")) {
262 | return runWithoutForking(args);
263 | }
264 | else {
265 | if (!forkMode.equals("once")) {
266 | getLog().error("Invalid forkMode: \"" + forkMode + "\"; Using once instead.");
267 | }
268 | return runForkingOnce(args);
269 | }
270 | }
271 |
272 | // Returns true if all tests pass
273 | private boolean runWithoutForking(String[] args) {
274 | try {
275 | return (Boolean) run().invoke(null, new Object[]{args});
276 | } catch (IllegalAccessException e) {
277 | throw new IllegalStateException(e);
278 | } catch (InvocationTargetException e) {
279 | Throwable target = e.getTargetException();
280 | if(target instanceof RuntimeException){
281 | throw (RuntimeException)target;
282 | } else {
283 | throw new IllegalArgumentException(target);
284 | }
285 | }
286 | }
287 |
288 | // Returns true if all tests pass
289 | private boolean runForkingOnce(String[] args) throws MojoFailureException {
290 |
291 | final Commandline cli = new Commandline();
292 | if ((this.workingDirectory == null || this.workingDirectory.isEmpty())) {
293 | cli.setWorkingDirectory(project.getBasedir());
294 | } else {
295 | cli.setWorkingDirectory(workingDirectory);
296 | }
297 |
298 | if (this.jvm == null || this.jvm.isEmpty()) {
299 | cli.setExecutable(getJvm());
300 | } else {
301 | cli.setExecutable(this.jvm);
302 | }
303 |
304 | // Set up environment
305 | if (environmentVariables != null) {
306 | for (final Map.Entry entry : environmentVariables.entrySet()) {
307 | cli.addEnvironment(entry.getKey(), entry.getValue());
308 | }
309 | }
310 | cli.addEnvironment("CLASSPATH", buildClassPathEnvironment());
311 |
312 | // Set up system properties
313 | if (systemProperties != null) {
314 | for (final Map.Entry entry : systemProperties.entrySet()) {
315 | cli.createArg().setValue(String.format("-D%s=%s", entry.getKey(), entry.getValue()));
316 | }
317 | }
318 | cli.createArg().setValue(String.format("-Dbasedir=%s", project.getBasedir().getAbsolutePath()));
319 |
320 | // Set user specified JVM arguments
321 | if (argLine != null) {
322 | cli.createArg().setLine(stripNewLines(argLine));
323 | }
324 |
325 | // Set debugging JVM arguments if debugging is enabled
326 | if (debugForkedProcess) {
327 | cli.createArg().setLine(stripNewLines(forkedProcessDebuggingArguments()));
328 | }
329 |
330 | // Set ScalaTest arguments
331 | cli.createArg().setValue("org.scalatest.tools.Runner");
332 | for (final String arg : args) {
333 | cli.createArg().setValue(arg);
334 | }
335 |
336 | // Log command string
337 | final String commandLogStatement = "Forking ScalaTest via: " + cli;
338 | if (logForkedProcessCommand) {
339 | getLog().info(commandLogStatement);
340 | } else {
341 | getLog().debug(commandLogStatement);
342 | }
343 |
344 | try (
345 | final Writer outputWriter = getOutputWriter()
346 | ) {
347 | final StreamConsumer outputConsumer = new WriterStreamConsumer(outputWriter);
348 |
349 | final int result = CommandLineUtils.executeCommandLine(cli, outputConsumer, outputConsumer,
350 | forkedProcessTimeoutInSeconds);
351 |
352 | return result == 0;
353 | }
354 | catch (final CommandLineTimeOutException e) {
355 | throw new MojoFailureException(String.format("Timed out after %d seconds waiting for forked process to complete.", forkedProcessTimeoutInSeconds), e);
356 | }
357 | catch (final CommandLineException e) {
358 | throw new MojoFailureException("Exception while executing forked process.", e);
359 | }
360 | catch (IOException e) {
361 | throw new MojoFailureException("Unable to close the output writer ", e);
362 | }
363 | }
364 |
365 | protected Writer getOutputWriter() throws MojoFailureException {
366 | return new PrintWriter(System.out) {
367 | @Override
368 | public void close() {
369 | out = null; // System.out should stay open
370 | }
371 | };
372 | }
373 |
374 | private String buildClassPathEnvironment() {
375 | StringBuffer buf = new StringBuffer();
376 | boolean first = true;
377 | for (String e : testClasspathElements()) {
378 | if (first) {
379 | first = false;
380 | }
381 | else {
382 | buf.append(File.pathSeparator);
383 | }
384 | buf.append(e);
385 | }
386 | return buf.toString();
387 | }
388 |
389 | private String forkedProcessDebuggingArguments() {
390 | if (debugArgLine == null) {
391 | return String.format("-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=%s", debuggerPort);
392 | } else {
393 | return debugArgLine;
394 | }
395 | }
396 |
397 | protected boolean isScalaTestAvailable() {
398 | try {
399 | classLoader().loadClass("org.scalatest.tools.Runner");
400 | return true;
401 | }
402 | catch (ClassNotFoundException e) {
403 | return false;
404 | }
405 | }
406 |
407 | // This is just used by runScalaTest to get the method to invoke
408 | private Method run() {
409 | try {
410 | Class> runner = classLoader().loadClass("org.scalatest.tools.Runner");
411 | return runner.getMethod("run", String[].class);
412 | } catch (NoSuchMethodException e) {
413 | throw new IllegalStateException(e);
414 | } catch (ClassNotFoundException e) {
415 | throw new IllegalStateException("scalatest is missing from classpath");
416 | }
417 | }
418 |
419 | // This is just used by run to get a class loader from which to load ScalaTest
420 | private ClassLoader classLoader() {
421 | try {
422 | List urls = new ArrayList();
423 | for (String element : testClasspathElements()) {
424 | File file = new File(element);
425 | if (file.isFile()) {
426 | urls.add(file.toURI().toURL());
427 | }
428 | }
429 | URL[] u = urls.toArray(new URL[urls.size()]);
430 | return new URLClassLoader(u);
431 | } catch (MalformedURLException e) {
432 | throw new IllegalStateException(e);
433 | }
434 | }
435 |
436 | // Have to use the programmatic way of getting the classpath elements
437 | // instead of the field-level injection since that apparently doesn't work
438 | // for ReporterMojos in maven-2.2 (it does work in maven-3)
439 | private List testClasspathElements() {
440 | try {
441 | return (List) project.getTestClasspathElements();
442 | }
443 | catch (DependencyResolutionRequiredException e) {
444 | // There's really no known way this exception can happen since
445 | // the @requiresDependencyResolution at the top of the class
446 | // defines test-scoped resolution.
447 | throw new IllegalStateException("Dependency resolution should be test-scoped.", e);
448 | }
449 | }
450 |
451 | // This is the configuration parameters shared by all concrete Mojo subclasses
452 | List sharedConfiguration() {
453 | return unmodifiableList(
454 | new ArrayList() {{
455 | addAll(runpath());
456 | addAll(config());
457 | addAll(tagsToInclude());
458 | addAll(tagsToExclude());
459 | addAll(parallel());
460 | addAll(tests());
461 | addAll(suites());
462 | addAll(suffixes());
463 | addAll(membersOnlySuites());
464 | addAll(wildcardSuites());
465 | addAll(testNGConfigFiles());
466 | addAll(memoryFiles());
467 | addAll(testsFiles());
468 | addAll(junitClasses());
469 | addAll(spanScaleFactor());
470 | }});
471 | }
472 |
473 | private List config() {
474 | List c = new ArrayList();
475 | for(String pair : splitOnComma(config)){
476 | c.add("-D"+pair);
477 | }
478 | return unmodifiableList(c);
479 | }
480 |
481 | private List runpath() {
482 | checkRunpathArgument("Output", outputDirectory);
483 | checkRunpathArgument("Test output", testOutputDirectory);
484 |
485 | String outputPath = outputDirectory.getAbsolutePath();
486 | if(outputPath.contains(" ")) {
487 | outputPath = outputPath.replaceAll(" ","\\\\ ");
488 | getLog().debug(String.format("Escaped output directory path: %s", outputPath));
489 | }
490 |
491 | String testOutputPath = testOutputDirectory.getAbsolutePath();
492 | if(testOutputPath.contains(" ")) {
493 | testOutputPath = testOutputPath.replaceAll(" ","\\\\ ");
494 | getLog().debug(String.format("Escaped test output directory path: %s", testOutputPath));
495 | }
496 |
497 | return compoundArg("-R",
498 | outputPath,
499 | testOutputPath,
500 | runpath);
501 | }
502 |
503 | private void checkRunpathArgument(String directoryName, File directory) {
504 | if(!directory.exists()) {
505 | getLog().warn(String.format("%s directory does not exist: %s", directoryName, directory.getAbsolutePath()));
506 | } else if(!directory.isDirectory()) {
507 | getLog().warn(String.format("%s is not a directory: %s", directoryName, directory.getAbsolutePath()));
508 | } else if(!directory.canRead()) {
509 | getLog().warn(String.format("%s directory is not readable: %s", directoryName, directory.getAbsolutePath()));
510 | }
511 | }
512 |
513 | private List tagsToInclude() {
514 | return compoundArg("-n", tagsToInclude);
515 | }
516 |
517 | private List tagsToExclude() {
518 | return compoundArg("-l", tagsToExclude);
519 | }
520 |
521 | private List parallel() {
522 | if (parallel) {
523 | String useSuiteSorting = suiteSorting ? "S" : "";
524 | String useThreadCount = threadCount == 0 ? "" : ("" + threadCount);
525 | return unmodifiableList(singletonList("-P" + useSuiteSorting + useThreadCount));
526 | }
527 | else
528 | return Collections.emptyList();
529 | }
530 |
531 | //
532 | // Generates a -s argument for each suite in comma-delimited list
533 | // 'suites', with optionally a -z or -t argument for a test name
534 | // if one follows the suite name.
535 | //
536 | // Test names follow suite names after whitespace, and may be prefixed
537 | // by an '@' sign to indicate they are an exact test name instead of
538 | // a substring. A -t argument is used for tests preceded by an '@'
539 | // sign, and -z is used for others.
540 | //
541 | private List suites() {
542 | List list = new ArrayList();
543 |
544 | for (String suite: splitOnComma(suites)) {
545 | SuiteTestPair pair = new SuiteTestPair(suite);
546 |
547 | if (pair.suite != null) {
548 | list.add("-s");
549 | list.add(pair.suite);
550 |
551 | if (pair.test != null) {
552 | addTest(list, pair.test);
553 | }
554 | }
555 | }
556 | return unmodifiableList(list);
557 | }
558 |
559 | //
560 | // Parses a string containing a Suite name followed
561 | // optionally by a test name.
562 | //
563 | // E.g. "HelloSuite hello there" would produce suite "HelloSuite"
564 | // and test "hello there".
565 | //
566 | static private class SuiteTestPair {
567 | String suite;
568 | String test;
569 |
570 | SuiteTestPair(String str) {
571 | if (str != null) {
572 | String trimStr = str.trim();
573 |
574 | if (trimStr.length() > 0) {
575 | String[] splits = trimStr.split("(?s)\\s", 2);
576 | if (splits.length > 1) {
577 | suite = splits[0];
578 | test = splits[1].trim();
579 | }
580 | else {
581 | suite = trimStr;
582 | }
583 | }
584 | }
585 | }
586 | }
587 |
588 | //
589 | // Adds a -t or -z arg for specified test name. Uses -t if name is
590 | // prefixed by an '@' sign, or -z otherwise.
591 | //
592 | private void addTest(List list, String testParm) {
593 | if (testParm != null) {
594 | String test = testParm.trim();
595 |
596 | if (test.length() > 0) {
597 | if (test.charAt(0) == '@') {
598 | String atTest = test.substring(1).trim();
599 |
600 | if (atTest.length() > 0) {
601 | list.add("-t");
602 | list.add(atTest);
603 | }
604 | }
605 | else {
606 | list.add("-z");
607 | list.add(test);
608 | }
609 | }
610 | }
611 | }
612 |
613 | //
614 | // Generates a -z or -t argument for each name in comma-delimited
615 | // 'tests' list, with -t used for those names prefixed by '@'.
616 | //
617 | private List tests() {
618 | List list = new ArrayList();
619 |
620 | for (String test: splitOnComma(tests)) {
621 | addTest(list, test);
622 | }
623 | return unmodifiableList(list);
624 | }
625 |
626 | private List spanScaleFactor() {
627 | List list = new ArrayList();
628 | if (spanScaleFactor != 1.0) {
629 | list.add("-F");
630 | list.add(spanScaleFactor + "");
631 | }
632 | return unmodifiableList(list);
633 | }
634 |
635 | private List suffixes() {
636 | return compoundArg("-q", suffixes);
637 | }
638 |
639 | private List membersOnlySuites() {
640 | return suiteArg("-m", membersOnlySuites);
641 | }
642 |
643 | private List wildcardSuites() {
644 | return suiteArg("-w", wildcardSuites);
645 | }
646 |
647 | private List testNGConfigFiles() {
648 | return suiteArg("-b", testNGConfigFiles);
649 | }
650 |
651 | private List memoryFiles() {
652 | return suiteArg("-M", memoryFiles);
653 | }
654 |
655 | private List testsFiles() {
656 | List list = new ArrayList();
657 | for (String param : splitOnComma(testsFiles)) {
658 | File file = new File(param);
659 | if (file.exists()) {
660 | list.add("-A");
661 | list.add(param);
662 | }
663 | }
664 | return unmodifiableList(list);
665 | }
666 |
667 | private List junitClasses() {
668 | return suiteArg("-j", jUnitClasses);
669 | }
670 | }
671 |
--------------------------------------------------------------------------------