From b36c3b5cdbebc12998b5e37150fea616401f9cb1 Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 7 May 2020 17:15:49 +0200 Subject: [PATCH] #764 Fix crash on parse some control chars --- test_runner/build.gradle.kts | 2 + test_runner/buildSrc/src/main/kotlin/Deps.kt | 4 ++ .../main/kotlin/ftl/reports/xml/JUnitXml.kt | 28 +++++++----- .../xml/preprocesor/XmlPreprocessor.kt | 14 ++++++ .../kotlin/ftl/reports/xml/JUnitXmlTest.kt | 43 +++++++++++++++++-- 5 files changed, 77 insertions(+), 14 deletions(-) create mode 100644 test_runner/src/main/kotlin/ftl/reports/xml/preprocesor/XmlPreprocessor.kt diff --git a/test_runner/build.gradle.kts b/test_runner/build.gradle.kts index 646ad2aaf4..c37db75413 100644 --- a/test_runner/build.gradle.kts +++ b/test_runner/build.gradle.kts @@ -213,6 +213,8 @@ dependencies { implementation(Libs.SYSTEM_RULES) testImplementation(Libs.TRUTH) testImplementation(Libs.MOCKK) + + implementation(Libs.COMMON_TEXT) } // Fix Exception in thread "main" java.lang.NoSuchMethodError: com.google.common.hash.Hashing.crc32c()Lcom/google/common/hash/HashFunction; diff --git a/test_runner/buildSrc/src/main/kotlin/Deps.kt b/test_runner/buildSrc/src/main/kotlin/Deps.kt index c445def81f..a0bb584313 100644 --- a/test_runner/buildSrc/src/main/kotlin/Deps.kt +++ b/test_runner/buildSrc/src/main/kotlin/Deps.kt @@ -74,6 +74,8 @@ object Versions { // https://github.com/mockk/mockk const val MOCKK = "1.9.3" + + const val COMMON_TEXT = "1.7" } object Libs { @@ -123,4 +125,6 @@ object Libs { const val TRUTH = "com.google.truth:truth:${Versions.TRUTH}" const val MOCKK = "io.mockk:mockk:${Versions.MOCKK}" //endregion + + const val COMMON_TEXT = "org.apache.commons:commons-text:${Versions.COMMON_TEXT}" } diff --git a/test_runner/src/main/kotlin/ftl/reports/xml/JUnitXml.kt b/test_runner/src/main/kotlin/ftl/reports/xml/JUnitXml.kt index b94676aa43..2289631f1e 100644 --- a/test_runner/src/main/kotlin/ftl/reports/xml/JUnitXml.kt +++ b/test_runner/src/main/kotlin/ftl/reports/xml/JUnitXml.kt @@ -6,16 +6,24 @@ import com.fasterxml.jackson.module.kotlin.KotlinModule import com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES import ftl.reports.xml.model.JUnitTestResult import ftl.reports.xml.model.JUnitTestSuite +import ftl.reports.xml.preprocesor.fixHtmlCodes import java.io.File +import java.nio.file.Files import java.nio.file.Path private val xmlModule = JacksonXmlModule().apply { setDefaultUseWrapper(false) } + private val xmlMapper = XmlMapper(xmlModule) .registerModules(KotlinModule()) .configure(FAIL_ON_UNKNOWN_PROPERTIES, false) internal val xmlPrettyWriter = xmlMapper.writerWithDefaultPrettyPrinter() +private fun xmlText(path: Path): String { + if (!path.toFile().exists()) throw RuntimeException("$path doesn't exist!") + return String(Files.readAllBytes(path)) +} + fun JUnitTestResult?.xmlToString(): String { if (this == null) return "" val prefix = "\n" @@ -23,29 +31,27 @@ fun JUnitTestResult?.xmlToString(): String { } fun parseOneSuiteXml(path: Path): JUnitTestResult { - return parseOneSuiteXml(path.toFile()) + return parseOneSuiteXml(xmlText(path)) } -fun parseOneSuiteXml(file: File): JUnitTestResult { - if (!file.exists()) throw RuntimeException("$file doesn't exist!") - return JUnitTestResult(mutableListOf(xmlMapper.readValue(file, JUnitTestSuite::class.java))) +fun parseOneSuiteXml(path: File): JUnitTestResult { + return parseOneSuiteXml(xmlText(path.toPath())) } fun parseOneSuiteXml(data: String): JUnitTestResult { - return JUnitTestResult(mutableListOf(xmlMapper.readValue(data, JUnitTestSuite::class.java))) + return JUnitTestResult(mutableListOf(xmlMapper.readValue(fixHtmlCodes(data), JUnitTestSuite::class.java))) } // -- fun parseAllSuitesXml(path: Path): JUnitTestResult { - return parseAllSuitesXml(path.toFile()) + return parseAllSuitesXml(xmlText(path)) } -fun parseAllSuitesXml(file: File): JUnitTestResult { - if (!file.exists()) throw RuntimeException("$file doesn't exist!") - return xmlMapper.readValue(file, JUnitTestResult::class.java) +fun parseAllSuitesXml(path: File): JUnitTestResult { + return parseAllSuitesXml(path.toPath()) } fun parseAllSuitesXml(data: String): JUnitTestResult { - return xmlMapper.readValue(data, JUnitTestResult::class.java) -} + return xmlMapper.readValue(fixHtmlCodes(data), JUnitTestResult::class.java) +} \ No newline at end of file diff --git a/test_runner/src/main/kotlin/ftl/reports/xml/preprocesor/XmlPreprocessor.kt b/test_runner/src/main/kotlin/ftl/reports/xml/preprocesor/XmlPreprocessor.kt new file mode 100644 index 0000000000..8dc28138a9 --- /dev/null +++ b/test_runner/src/main/kotlin/ftl/reports/xml/preprocesor/XmlPreprocessor.kt @@ -0,0 +1,14 @@ +package ftl.reports.xml.preprocesor + +import org.apache.commons.text.StringEscapeUtils + +fun fixHtmlCodes(data: String): String { + val isoHtmlCodesToReplace = listOf(0x00..0x1F).union(listOf(0x7F..0x9F)).flatten() + .map { StringEscapeUtils.escapeXml11(it.toChar().toString()) }.filter { it.startsWith("&#") } + + var fixedStr = data + for (isoControlCode in isoHtmlCodesToReplace) { + fixedStr = fixedStr.replace(isoControlCode, "") + } + return fixedStr +} \ No newline at end of file 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 9481d3b698..0ffd4229e4 100644 --- a/test_runner/src/test/kotlin/ftl/reports/xml/JUnitXmlTest.kt +++ b/test_runner/src/test/kotlin/ftl/reports/xml/JUnitXmlTest.kt @@ -5,6 +5,7 @@ import ftl.test.util.TestHelper.normalizeLineEnding import java.nio.file.Paths import org.junit.Test + class JUnitXmlTest { companion object { @@ -73,7 +74,8 @@ junit.framework.Assert.fail(Assert.java:50) @Test fun `merge ios`() { - val merged = parseAllSuitesXml(iosPassXml).merge(parseAllSuitesXml(iosFailXml)).xmlToString().normalizeLineEnding() + val merged = + parseAllSuitesXml(iosPassXml).merge(parseAllSuitesXml(iosFailXml)).xmlToString().normalizeLineEnding() val expected = """ @@ -94,7 +96,8 @@ junit.framework.Assert.fail(Assert.java:50) @Test fun `Merge iOS large time`() { - val merged = parseAllSuitesXml(iosLargeNum).merge(parseAllSuitesXml(iosLargeNum)).xmlToString().normalizeLineEnding() + val merged = + parseAllSuitesXml(iosLargeNum).merge(parseAllSuitesXml(iosLargeNum)).xmlToString().normalizeLineEnding() val expected = """ @@ -423,7 +426,8 @@ junit.framework.Assert.fail(Assert.java:50) // * 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 = parseAllSuitesXml(newRun).mergeTestTimes(parseAllSuitesXml(oldRun)).xmlToString().normalizeLineEnding() + val merged = + parseAllSuitesXml(newRun).mergeTestTimes(parseAllSuitesXml(oldRun)).xmlToString().normalizeLineEnding() val expected = """ @@ -437,4 +441,37 @@ junit.framework.Assert.fail(Assert.java:50) """.trimIndent() assertThat(merged).isEqualTo(expected) } + + + @Test + fun `parse ftl quirks`() { + val crashingAllSuitesMessage = """ + + + + + java.net.ConnectException: Failed to connect to ... at (Coroutine boundary.() + + + + + + """.trimIndent() + val crashingOneSuiteMessage = """ + + + + java.net.ConnectException: Failed to connect to ... at (Coroutine boundary.() + + + + + """.trimIndent() + + parseAllSuitesXml(crashingAllSuitesMessage) + parseOneSuiteXml(crashingOneSuiteMessage) + + } + } +