diff --git a/main/src/main/scala/sbt/plugins/JUnitXmlReportPlugin.scala b/main/src/main/scala/sbt/plugins/JUnitXmlReportPlugin.scala index 39baa07a6..2e194cc3f 100644 --- a/main/src/main/scala/sbt/plugins/JUnitXmlReportPlugin.scala +++ b/main/src/main/scala/sbt/plugins/JUnitXmlReportPlugin.scala @@ -8,9 +8,16 @@ package sbt package plugins -import Def.Setting +import java.io.File + +import Def.{ Setting, settingKey } +import Defaults._ import Keys._ -import sbt.internal.SysProp +import KeyRanks._ +import sbt.Project.inConfig +import sbt.internal._ +import sbt.io.syntax._ +import sbt.librarymanagement.Configurations.{ IntegrationTest, Test } /** * An experimental plugin that adds the ability for junit-xml to be generated. @@ -27,14 +34,23 @@ object JUnitXmlReportPlugin extends AutoPlugin { override def requires = JvmPlugin override def trigger = allRequirements - // Right now we add to the global test listeners which should capture *all* tests. - // It might be a good idea to derive this setting into specific test scopes. - override lazy val projectSettings: Seq[Setting[_]] = - Seq( + object autoImport { + val testReportsDirectory = + settingKey[File]("Directory for outputting junit test reports.").withRank(AMinusSetting) + + lazy val testReportSettings: Seq[Setting[_]] = Seq( + testReportsDirectory := target.value / (prefix(configuration.value.name) + "reports"), testListeners += new JUnitXmlTestsListener( - target.value.getAbsolutePath, + testReportsDirectory.value, SysProp.legacyTestReport, streams.value.log ) ) + } + + import autoImport._ + + override lazy val projectSettings: Seq[Setting[_]] = + inConfig(Test)(testReportSettings) ++ + inConfig(IntegrationTest)(testReportSettings) } diff --git a/notes/1.6.0/configure-junit-xml-reports-dir.md b/notes/1.6.0/configure-junit-xml-reports-dir.md new file mode 100644 index 000000000..b05f44006 --- /dev/null +++ b/notes/1.6.0/configure-junit-xml-reports-dir.md @@ -0,0 +1,15 @@ +[@ashleymercer]: https://github.com/ashleymercer + +[#2853]: https://github.com/sbt/sbt/issues/2853 + +### Fixes with compatibility implications + +- Add a new setting `testReportsDirectory` to allow output directory for JUnitXmlTestsListener to be configured, + and expose `testReportSettings` which provides defaults for new configurations [#2853][] by [@ashleymercer][] + - output directory now uses the configuration name as a prefix so `target/test-reports` for the `Test` configuration + but `target/it-reports` for the `IntegrationTest` configuration (previously this was hardcoded to always use + `target/test-reports`). To override this set e.g. `Test / testReportsDirectory := target.value / "my-custom-dir"` + - the `JunitXmlTestsListener` is now only attached to the `Test` and `IntegrationTest` configurations by default + (previously it was added to the global configuration object). Any configurations which inherit from one of these + will therefore continue to have the listener attached; but completely custom configurations will need to re-add + it with `.settings(testReportSettings)` \ No newline at end of file diff --git a/testing/src/main/scala/sbt/JUnitXmlTestsListener.scala b/testing/src/main/scala/sbt/JUnitXmlTestsListener.scala index 1a3cf42e9..95bbee3b6 100644 --- a/testing/src/main/scala/sbt/JUnitXmlTestsListener.scala +++ b/testing/src/main/scala/sbt/JUnitXmlTestsListener.scala @@ -31,14 +31,20 @@ import sbt.protocol.testing.TestResult /** * A tests listener that outputs the results it receives in junit xml * report format. - * @param outputDir path to the dir in which a folder with results is generated + * @param targetDir directory in which test reports are generated */ -class JUnitXmlTestsListener(val outputDir: String, legacyTestReport: Boolean, logger: Logger) +class JUnitXmlTestsListener(val targetDir: File, legacyTestReport: Boolean, logger: Logger) extends TestsListener { - // These constructors are for binary compatibility with older versions of sbt. + // These constructors are for binary compatibility with older versions of sbt + // Use old hard-coded behaviour for constructing `targetDir` from `outputDir` + def this(outputDir: String, legacyTestReport: Boolean, logger: Logger) = + this(new File(outputDir, "test-reports"), legacyTestReport, logger) def this(outputDir: String, logger: Logger) = this(outputDir, false, logger) def this(outputDir: String) = this(outputDir, false, null) + @deprecated("Provided for binary compatibility: please use `targetDir` instead", "1.6.0") + def outputDir: String = targetDir.getParent + /**Current hostname so we know which machine executed the tests*/ val hostname: String = { val start = System.nanoTime @@ -57,9 +63,6 @@ class JUnitXmlTestsListener(val outputDir: String, legacyTestReport: Boolean, lo name } - /**The dir in which we put all result files. Is equal to the given dir + "/test-reports"*/ - val targetDir = new File(outputDir + "/test-reports/") - /**all system properties as XML*/ val properties: Elem = @@ -114,8 +117,8 @@ class JUnitXmlTestsListener(val outputDir: String, legacyTestReport: Boolean, lo } skipped={ignoredSkippedPending + ""} time={(duration / 1000.0).toString} timestamp={ formatISO8601DateTime(timestamp) }> - {properties} - { + {properties} + { for (e <- events) yield s"(It is not a test it is a ${other.getClass.getCanonicalName})" } } time={(e.duration() / 1000.0).toString}> - { + { val trace: String = if (e.throwable.isDefined) { val stringWriter = new StringWriter() val writer = new PrintWriter(stringWriter) @@ -157,12 +160,12 @@ class JUnitXmlTestsListener(val outputDir: String, legacyTestReport: Boolean, lo case _ => {} } } - + } - - - + + + result }