Fix for JUnitXmlTestsListener that is removing part of test name when it contains dot (#5139)

Fix for JUnitXmlTestsListener removing part of test when it contains dot
Fixes https://github.com/sbt/sbt/issues/5114
Fixes https://github.com/sbt/sbt/issues/2949
This commit is contained in:
Margus Sipria 2019-09-30 17:47:15 +03:00 committed by eugene yokota
parent 0bdde4aee8
commit 108611ca25
3 changed files with 62 additions and 19 deletions

View File

@ -26,7 +26,16 @@ lazy val root = (project in file(".")).
if( oneSecondReport.label != "testsuite" ) sys.error("Report should have a root <testsuite> element.")
// somehow the 'success' event doesn't go through... TODO investigate
// if( (oneSecondReport \ "@time").text.toFloat < 1f ) sys.error("expected test to take at least 1 sec")
if( (oneSecondReport \ "@tests").text != "1" ) sys.error("expected 1 tests")
if( (oneSecondReport \ "@failures").text != "0" ) sys.error("expected 0 failures")
if( (oneSecondReport \ "@name").text != "a.pkg.OneSecondTest" ) sys.error("wrong fixture name: " + (oneSecondReport \ "@name").text)
oneSecondReport foreach { testsuite =>
val className = testsuite \ "testcase" \@ "classname"
if( className != "a.pkg.OneSecondTest" ) sys.error(s"wrong class name: $className")
val actualTestName = testsuite \ "testcase" \@ "name"
if( actualTestName != "oneSecond") sys.error(s"wrong test names: $actualTestName")
}
// TODO more checks
val failingReport = XML.loadFile(failingReportFile)
@ -37,17 +46,36 @@ lazy val root = (project in file(".")).
val scalaTestFlatReport = XML.loadFile(flatSuiteReportFile)
if( scalaTestFlatReport.label != "testsuite" ) sys.error("Report should have a root <testsuite> element.")
if( (scalaTestFlatReport \ "@tests").text != "2" ) sys.error("expected 2 tests")
if( (scalaTestFlatReport \ "@tests").text != "3" ) sys.error("expected 3 tests")
if( (scalaTestFlatReport \ "@failures").text != "1" ) sys.error("expected 1 failures")
if( (scalaTestFlatReport \ "@name").text != "my.scalatest.MyFlatSuite" ) sys.error("wrong fixture name: " + (scalaTestFlatReport \ "@name").text)
scalaTestFlatReport foreach { testsuite =>
val classNames = (testsuite \ "testcase").map(_ \@ "classname")
if( classNames.length != 3 ) sys.error(s"expected 3 classname's, actual result: " + classNames.length)
if( classNames.toSet != Set("my.scalatest.MyFlatSuite") ) sys.error(s"wrong class names: ${classNames.mkString(", ")}")
val expectedTestNames = Set(
"Passing test should pass",
"Passing test should also pass with file.extension",
"Failing test should fail"
)
val actualTestName = (testsuite \ "testcase").map(_ \@ "name")
if( actualTestName.toSet != expectedTestNames) sys.error(s"wrong test names: ${actualTestName.mkString(", ")}")
}
val nestedSuitesReport = XML.loadFile(nestedSuitesReportFile)
if( nestedSuitesReport.label != "testsuite" ) sys.error("Report should have a root <testsuite> element.")
if( (nestedSuitesReport \ "@tests").text != "2" ) sys.error("expected 2 tests")
if( (nestedSuitesReport \ "@failures").text != "1" ) sys.error("expected 1 failures")
if( (nestedSuitesReport \ "@name").text != "my.scalatest.MyNestedSuites" ) sys.error("wrong fixture name: " + (nestedSuitesReport \ "@name").text)
val actualTestName = (nestedSuitesReport \ "testcase").map(t => (t \ "@name").text)
if( actualTestName.toSet != Set("MyInnerSuite.Inner passing test should pass", "MyInnerSuite.Inner failing test should fail")) sys.error(s"wrong test names: ${actualTestName.mkString(", ")}")
nestedSuitesReport foreach { testsuite =>
val classNames = (testsuite \ "testcase").map(_ \@ "classname")
if( classNames.length != 2 ) sys.error(s"expected 2 classname's, actual result: " + classNames.length)
if( classNames.toSet != Set("my.scalatest.MyInnerSuite") ) sys.error(s"wrong class names: ${classNames.mkString(", ")}")
val actualTestName = (testsuite \ "testcase").map(_ \@ "name")
if( actualTestName.toSet != Set("Inner passing test should pass", "Inner failing test should fail")) sys.error(s"wrong test names: ${actualTestName.mkString(", ")}")
}
// TODO check console output is in the report
},

View File

@ -6,11 +6,16 @@ package my.scalatest {
}
it should "also pass with file.extension" in {
}
"Failing test" should "fail" in {
sys.error("wah wah")
}
}
@DoNotDiscover
class MyInnerSuite(arg: String) extends FlatSpec {
"Inner passing test" should "pass" in {

View File

@ -38,7 +38,7 @@ class JUnitXmlTestsListener(val outputDir: String, logger: Logger) extends Tests
def this(outputDir: String) = this(outputDir, null)
/**Current hostname so we know which machine executed the tests*/
val hostname = {
val hostname: String = {
val start = System.nanoTime
val name = try InetAddress.getLocalHost.getHostName
catch {
@ -59,7 +59,7 @@ class JUnitXmlTestsListener(val outputDir: String, logger: Logger) extends Tests
val targetDir = new File(outputDir + "/test-reports/")
/**all system properties as XML*/
val properties =
val properties: Elem =
<properties>
{
// create a clone, defending against [[ConcurrentModificationException]]
@ -84,7 +84,7 @@ class JUnitXmlTestsListener(val outputDir: String, logger: Logger) extends Tests
val events: ListBuffer[TEvent] = new ListBuffer()
/**Adds one test result to this suite.*/
def addEvent(e: TEvent) = events += e
def addEvent(e: TEvent): ListBuffer[TEvent] = events += e
/** Returns the number of tests of each state for the specified. */
def count(status: TStatus) = events.count(_.status == status)
@ -103,6 +103,9 @@ class JUnitXmlTestsListener(val outputDir: String, logger: Logger) extends Tests
TStatus.Pending
)
// for sbt/junit-interface version 0.11 (in future versions this should be done there)
val classnameRegex = s"^($name|${name.split('.').last})\\.?".r
val result =
<testsuite hostname={hostname} name={name} tests={tests + ""} errors={errors + ""} failures={
failures + ""
@ -112,12 +115,19 @@ class JUnitXmlTestsListener(val outputDir: String, logger: Logger) extends Tests
{properties}
{
for (e <- events)
yield <testcase classname={name} name={
yield <testcase classname={
e.selector match {
case selector: TestSelector => selector.testName.split('.').last
case nested: NestedTestSelector =>
nested.suiteId().split('.').last + "." + nested.testName()
case other => s"(It is not a test it is a ${other.getClass.getCanonicalName})"
case nested: NestedTestSelector => nested.suiteId()
case _ => name
}
} name={
e.selector match {
case selector: TestSelector =>
val matchEnd =
classnameRegex.findFirstMatchIn(selector.testName).map(_.end).getOrElse(0)
selector.testName.substring(matchEnd)
case nested: NestedTestSelector => nested.testName()
case other => s"(It is not a test it is a ${other.getClass.getCanonicalName})"
}
} time={(e.duration() / 1000.0).toString}>
{
@ -161,11 +171,11 @@ class JUnitXmlTestsListener(val outputDir: String, logger: Logger) extends Tests
override def initialValue(): Option[TestSuite] = None
}
private def withTestSuite[T](f: TestSuite => T) =
private def withTestSuite[T](f: TestSuite => T): T =
testSuite.get().map(f).getOrElse(sys.error("no test suite"))
/**Creates the output Dir*/
override def doInit() = {
override def doInit(): Unit = {
val _ = targetDir.mkdirs()
}
@ -203,9 +213,9 @@ class JUnitXmlTestsListener(val outputDir: String, logger: Logger) extends Tests
* <system-err><![CDATA[]]></system-err>
* </testsuite>
*/
override def endGroup(name: String, t: Throwable) = {
override def endGroup(name: String, t: Throwable): Unit = {
// create our own event to record the error
val event = new TEvent {
val event: TEvent = new TEvent {
def fullyQualifiedName = name
//def description =
//"Throwable escaped the test run of '%s'".format(name)
@ -223,7 +233,7 @@ class JUnitXmlTestsListener(val outputDir: String, logger: Logger) extends Tests
* Ends the current suite, wraps up the result and writes it to an XML file
* in the output folder that is named after the suite.
*/
override def endGroup(name: String, result: TestResult) = {
override def endGroup(name: String, result: TestResult): Unit = {
writeSuite()
}
@ -237,7 +247,7 @@ class JUnitXmlTestsListener(val outputDir: String, logger: Logger) extends Tests
private[this] def formatISO8601DateTime(d: LocalDateTime): String =
d.truncatedTo(ChronoUnit.SECONDS).format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
private def writeSuite() = {
private def writeSuite(): Unit = {
val legacyFile =
new File(targetDir, s"${normalizeName(withTestSuite(_.name))}.xml").getAbsolutePath
val file =
@ -245,8 +255,8 @@ class JUnitXmlTestsListener(val outputDir: String, logger: Logger) extends Tests
// TODO would be nice to have a logger and log this with level debug
// System.err.println("Writing JUnit XML test report: " + file)
val testSuiteResult = withTestSuite(_.stop())
XML.save(legacyFile, testSuiteResult, "UTF-8", true, null)
XML.save(file, testSuiteResult, "UTF-8", true, null)
XML.save(legacyFile, testSuiteResult, "UTF-8", xmlDecl = true, null)
XML.save(file, testSuiteResult, "UTF-8", xmlDecl = true, null)
testSuite.remove()
}