From 293ef05fa0b576f9b0c51084f312232bb6f3be59 Mon Sep 17 00:00:00 2001 From: bootstraponline Date: Mon, 12 Nov 2018 20:30:12 -0500 Subject: [PATCH] Add mergeTestTimes --- .../ftl/reports/xml/model/JUnitTestCase.kt | 12 +++- .../ftl/reports/xml/model/JUnitTestResult.kt | 13 ++++- .../ftl/reports/xml/model/JUnitTestSuite.kt | 55 +++++++++++++++++++ .../kotlin/ftl/reports/xml/JUnitXmlTest.kt | 51 +++++++++++++++++ 4 files changed, 128 insertions(+), 3 deletions(-) diff --git a/test_runner/src/main/kotlin/ftl/reports/xml/model/JUnitTestCase.kt b/test_runner/src/main/kotlin/ftl/reports/xml/model/JUnitTestCase.kt index d04715f995..0a8da493f3 100644 --- a/test_runner/src/main/kotlin/ftl/reports/xml/model/JUnitTestCase.kt +++ b/test_runner/src/main/kotlin/ftl/reports/xml/model/JUnitTestCase.kt @@ -24,11 +24,11 @@ data class JUnitTestCase( // JUnit XML allows arbitrary amounts of failure/error tags @JsonInclude(JsonInclude.Include.NON_NULL) @JacksonXmlProperty(localName = "failure") - val failures: List?, + val failures: List? = null, @JsonInclude(JsonInclude.Include.NON_NULL) @JacksonXmlProperty(localName = "error") - val errors: List?, + val errors: List? = null, @JsonInclude(JsonInclude.Include.CUSTOM, valueFilter = FilterNotNull::class) val skipped: String? = "absent" // used by FilterNotNull to filter out absent `skipped` values @@ -41,6 +41,14 @@ data class JUnitTestCase( return failures?.isNotEmpty() == true || errors?.isNotEmpty() == true } + fun skipped(): Boolean { + return skipped == null + } + + fun successful(): Boolean { + return failed().not().and(skipped().not()) + } + fun stackTrace(): String { return failures?.joinToString() + errors?.joinToString() } diff --git a/test_runner/src/main/kotlin/ftl/reports/xml/model/JUnitTestResult.kt b/test_runner/src/main/kotlin/ftl/reports/xml/model/JUnitTestResult.kt index 2e81be4c2e..6149e2d271 100644 --- a/test_runner/src/main/kotlin/ftl/reports/xml/model/JUnitTestResult.kt +++ b/test_runner/src/main/kotlin/ftl/reports/xml/model/JUnitTestResult.kt @@ -12,7 +12,18 @@ data class JUnitTestResult( var testsuites: MutableList? ) { fun mergeTestTimes(other: JUnitTestResult?): JUnitTestResult { - // TODO: ... + if (other == null) return this + if (this.testsuites == null) this.testsuites = mutableListOf() + + // newTestResult.mergeTestTimes(oldTestResult) + // + // for each new JUnitTestSuite, check if it exists on old + // if JUnitTestSuite exists on both then merge test times + this.testsuites?.forEach { testSuite -> + val oldSuite = other.testsuites?.firstOrNull { it.name == testSuite.name } + if (oldSuite != null) testSuite.mergeTestTimes(oldSuite) + } + return this } diff --git a/test_runner/src/main/kotlin/ftl/reports/xml/model/JUnitTestSuite.kt b/test_runner/src/main/kotlin/ftl/reports/xml/model/JUnitTestSuite.kt index 5faebcce65..c2bc7be27f 100644 --- a/test_runner/src/main/kotlin/ftl/reports/xml/model/JUnitTestSuite.kt +++ b/test_runner/src/main/kotlin/ftl/reports/xml/model/JUnitTestSuite.kt @@ -71,4 +71,59 @@ data class JUnitTestSuite( return this } + + fun mergeTestTimes(other: JUnitTestSuite): JUnitTestSuite { + if (this.name != other.name) throw RuntimeException("Attempted to merge ${other.name} into ${this.name}") + + // for each new JUnitTestCase + // iif it failed then pull timing info from old + // remove if not successful in either new or old + + // if we ran no test cases then don't bother merging old times. + if (this.testcases == null) return this + + val mergedTestCases = mutableListOf() + var mergedTime = 0.0 + + this.testcases?.forEach { testcase -> + // if test was skipped, then continue to skip it. + if (testcase.skipped()) return@forEach + + // if the test succeeded, use the new time value + if (testcase.successful()) { + mergedTime += testcase.time.toDouble() + mergedTestCases.add( + JUnitTestCase( + name = testcase.name, + classname = testcase.classname, + time = testcase.time + ) + ) + return@forEach + } + + // if the test we ran failed, copy timing from the last successful run + val lastSuccessfulRun = other.testcases?.firstOrNull { + it.successful() && it.name == testcase.name && it.classname == testcase.classname + } ?: return@forEach + + mergedTime += lastSuccessfulRun.time.toDouble() + mergedTestCases.add( + JUnitTestCase( + name = testcase.name, + classname = testcase.classname, + time = lastSuccessfulRun.time + ) + ) + } + + this.testcases = mergedTestCases + this.tests = mergedTestCases.size.toString() + this.failures = "0" + this.errors = "0" + this.skipped = "0" + this.time = mergedTime.toString() + + return this + } } diff --git a/test_runner/src/test/kotlin/ftl/reports/xml/JUnitXmlTest.kt b/test_runner/src/test/kotlin/ftl/reports/xml/JUnitXmlTest.kt index e70368e64e..f96efe5048 100644 --- a/test_runner/src/test/kotlin/ftl/reports/xml/JUnitXmlTest.kt +++ b/test_runner/src/test/kotlin/ftl/reports/xml/JUnitXmlTest.kt @@ -312,4 +312,55 @@ junit.framework.Assert.fail(Assert.java:50) } } } + + @Test + fun merge_testTimes() { + val newRun = """ + + + + + + + Exception: NoMatchingElementException + failed: caught "EarlGreyInternalTestInterruptException", "Immediately halt execution of testcase" + + + + + + + """.trimIndent() + + val oldRun = """ + + + + + + + + + + """.trimIndent() + + // new run has 2 passing, 1 failure, and 1 skipped + // * a() and b() passed in newRun and are copied over + // * c() failed in newRun and passed in oldRun. timing info copied over from oldRun + // * d() was skipped in newRun and successful in oldRun. d() is excluded from the merged result + + val merged = parseIosXml(newRun).mergeTestTimes(parseIosXml(oldRun)).xmlToString() + val expected = """ + + + + + + + + + + """.trimIndent() + assertThat(merged).isEqualTo(expected) + } }