From a382bef144f84ef200b6b5671fc94811ade57b6d Mon Sep 17 00:00:00 2001 From: Yunir Salimzyanov Date: Wed, 17 Jun 2020 20:08:45 +0300 Subject: [PATCH] Extract classes and related functions of muteWithDatabase to separate module --- .../jetbrains/kotlin/test/muteWithDatabase.kt | 145 +----------------- .../jetbrains/kotlin/test/mutes/AutoMute.kt | 36 +++++ .../jetbrains/kotlin/test/mutes/MutedSet.kt | 37 +++++ .../jetbrains/kotlin/test/mutes/MutedTest.kt | 93 +++++++++++ 4 files changed, 167 insertions(+), 144 deletions(-) create mode 100644 compiler/tests-mutes/src/org/jetbrains/kotlin/test/mutes/AutoMute.kt create mode 100644 compiler/tests-mutes/src/org/jetbrains/kotlin/test/mutes/MutedSet.kt create mode 100644 compiler/tests-mutes/src/org/jetbrains/kotlin/test/mutes/MutedTest.kt diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/test/muteWithDatabase.kt b/compiler/tests-common/tests/org/jetbrains/kotlin/test/muteWithDatabase.kt index 20555fec7a8..383a3a73ede 100644 --- a/compiler/tests-common/tests/org/jetbrains/kotlin/test/muteWithDatabase.kt +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/test/muteWithDatabase.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.test import junit.framework.TestCase +import org.jetbrains.kotlin.test.mutes.* import org.junit.internal.runners.statements.InvokeMethod import org.junit.runner.Runner import org.junit.runner.notification.Failure @@ -16,149 +17,9 @@ import org.junit.runners.model.Statement import org.junit.runners.parameterized.BlockJUnit4ClassRunnerWithParameters import org.junit.runners.parameterized.ParametersRunnerFactory import org.junit.runners.parameterized.TestWithParameters -import java.io.File - -private class AutoMute( - val file: String, - val issue: String -) private val SKIP_MUTED_TESTS = java.lang.Boolean.getBoolean("org.jetbrains.kotlin.skip.muted.tests") -private val DO_AUTO_MUTE: AutoMute? by lazy { - val autoMuteFile = File("tests/automute") - if (autoMuteFile.exists()) { - val lines = autoMuteFile.readLines().filter { it.isNotBlank() }.map { it.trim() } - AutoMute( - lines.getOrNull(0) ?: error("A file path is expected in tne first line"), - lines.getOrNull(1) ?: error("An issue description is the second line") - ) - } else { - null - } -} - -private fun AutoMute.muteTest(testKey: String) { - val file = File(file) - val lines = file.readLines() - val firstLine = lines[0] // Drop file header - val muted = lines.drop(1).toMutableList() - muted.add("$testKey, $issue") - val newMuted: List = mutableListOf() + firstLine + muted.sorted() - file.writeText(newMuted.joinToString("\n")) -} - -private class MutedTest( - val key: String, - @Suppress("unused") val issue: String?, - val hasFailFile: Boolean, - val isFlaky: Boolean -) { - val methodKey: String - val classNameKey: String - val simpleClassName: String - - init { - val noQuoteKey = key.replace("`", "") - val beforeParamsKey = noQuoteKey.substringBefore("[") - val params = noQuoteKey.substringAfterWithDelimiter("[", "") - - methodKey = (beforeParamsKey.substringAfterLast(".", "") + params) - .also { - if (it.isEmpty()) throw IllegalArgumentException("Can't get method name: '$key'") - } - - classNameKey = beforeParamsKey.substringBeforeLast(".", "").also { - if (it.isEmpty()) throw IllegalArgumentException("Can't get class name: '$key'") - } - - simpleClassName = classNameKey.substringAfterLast(".") - } - - companion object { - fun String.substringAfterWithDelimiter(delimiter: String, missingDelimiterValue: String = this): String { - val index = indexOf(delimiter) - return if (index == -1) missingDelimiterValue else (delimiter + substring(index + 1, length)) - } - } -} - -private class MutedSet(muted: List) { - // Method key -> Simple class name -> List of muted tests - private val cache: Map>> = - muted - .groupBy { it.methodKey } // Method key -> List of muted tests - .mapValues { (_, tests) -> tests.groupBy { it.simpleClassName } } - - fun mutedTest(testClass: Class<*>, methodKey: String): MutedTest? { - val mutedTests = cache[methodKey]?.get(testClass.simpleName) ?: return null - - return mutedTests.firstOrNull { mutedTest -> - testClass.canonicalName.endsWith(mutedTest.classNameKey) - } - } -} - -private fun loadMutedSet(files: List): MutedSet { - return MutedSet(files.flatMap { file -> loadMutedTests(file) }) -} - -private fun loadMutedTests(file: File): List { - if (!file.exists()) { - System.err.println("Can't find mute file: ${file.absolutePath}") - return listOf() - } - - try { - val testLines = file.readLines() - .asSequence() - .map { it.trim() } - .filter { it.isNotEmpty() } - .toList() - - return testLines.drop(1).map { parseMutedTest(it) } - } catch (ex: Throwable) { - throw ParseError("Couldn't parse file with muted tests: $file", cause = ex) - } -} - -private val COLUMN_PARSE_REGEXP = Regex("\\s*(?:(?:\"((?:[^\"]|\"\")*)\")|([^,]*))\\s*") -private val MUTE_LINE_PARSE_REGEXP = Regex("$COLUMN_PARSE_REGEXP,$COLUMN_PARSE_REGEXP,$COLUMN_PARSE_REGEXP,$COLUMN_PARSE_REGEXP") - -private fun parseMutedTest(str: String): MutedTest { - val matchResult = MUTE_LINE_PARSE_REGEXP.matchEntire(str) ?: throw ParseError("Can't parse the line: $str") - val resultValues = matchResult.groups.filterNotNull() - - val testKey = resultValues[1].value - val issue = resultValues[2].value - val stateStr = resultValues[3].value - val statusStr = resultValues[4].value - - val hasFailFile = when (stateStr) { - "MUTE", "" -> false - "FAIL" -> true - else -> throw ParseError("Invalid state (`$stateStr`), MUTE, FAIL or empty are expected: $str") - } - val isFlaky = when (statusStr) { - "FLAKY" -> true - "" -> false - else -> throw ParseError("Invalid status (`$statusStr`), FLAKY or empty are expected: $str") - } - - return MutedTest(testKey, issue, hasFailFile, isFlaky) -} - -private class ParseError(message: String, override val cause: Throwable? = null) : IllegalArgumentException(message) - -private val mutedSet by lazy { - loadMutedSet( - listOf( - File("tests/mute-common.csv"), - File("tests/mute-platform.csv") - ) - ) -} - private fun isMutedInDatabase(testCase: TestCase): Boolean { return isMutedInDatabase(testCase.javaClass, testCase.name) } @@ -172,10 +33,6 @@ private fun getMutedTest(testCase: TestCase): MutedTest? { return getMutedTest(testCase.javaClass, testCase.name) } -private fun getMutedTest(testClass: Class<*>, methodKey: String): MutedTest? { - return mutedSet.mutedTest(testClass, methodKey) -} - internal fun wrapWithMuteInDatabase(testCase: TestCase, f: () -> Unit): (() -> Unit)? { if (isMutedInDatabase(testCase)) { return { diff --git a/compiler/tests-mutes/src/org/jetbrains/kotlin/test/mutes/AutoMute.kt b/compiler/tests-mutes/src/org/jetbrains/kotlin/test/mutes/AutoMute.kt new file mode 100644 index 00000000000..ff3e6d1e2b0 --- /dev/null +++ b/compiler/tests-mutes/src/org/jetbrains/kotlin/test/mutes/AutoMute.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.test.mutes + +import java.io.File + +class AutoMute( + val file: String, + val issue: String +) + +val DO_AUTO_MUTE: AutoMute? by lazy { + val autoMuteFile = File("tests/automute") + if (autoMuteFile.exists()) { + val lines = autoMuteFile.readLines().filter { it.isNotBlank() }.map { it.trim() } + AutoMute( + lines.getOrNull(0) ?: error("A file path is expected in tne first line"), + lines.getOrNull(1) ?: error("An issue description is the second line") + ) + } else { + null + } +} + +fun AutoMute.muteTest(testKey: String) { + val file = File(file) + val lines = file.readLines() + val firstLine = lines[0] // Drop file header + val muted = lines.drop(1).toMutableList() + muted.add("$testKey, $issue") + val newMuted: List = mutableListOf() + firstLine + muted.sorted() + file.writeText(newMuted.joinToString("\n")) +} \ No newline at end of file diff --git a/compiler/tests-mutes/src/org/jetbrains/kotlin/test/mutes/MutedSet.kt b/compiler/tests-mutes/src/org/jetbrains/kotlin/test/mutes/MutedSet.kt new file mode 100644 index 00000000000..11b36416e3c --- /dev/null +++ b/compiler/tests-mutes/src/org/jetbrains/kotlin/test/mutes/MutedSet.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.test.mutes + +import java.io.File + +class MutedSet(muted: List) { + // Method key -> Simple class name -> List of muted tests + private val cache: Map>> = + muted + .groupBy { it.methodKey } // Method key -> List of muted tests + .mapValues { (_, tests) -> tests.groupBy { it.simpleClassName } } + + fun mutedTest(testClass: Class<*>, methodKey: String): MutedTest? { + val mutedTests = cache[methodKey]?.get(testClass.simpleName) ?: return null + + return mutedTests.firstOrNull { mutedTest -> + testClass.canonicalName.endsWith(mutedTest.classNameKey) + } + } +} + +private fun loadMutedSet(files: List): MutedSet { + return MutedSet(files.flatMap { file -> loadMutedTests(file) }) +} + +val mutedSet by lazy { + loadMutedSet( + listOf( + File("tests/mute-common.csv"), + File("tests/mute-platform.csv") + ) + ) +} \ No newline at end of file diff --git a/compiler/tests-mutes/src/org/jetbrains/kotlin/test/mutes/MutedTest.kt b/compiler/tests-mutes/src/org/jetbrains/kotlin/test/mutes/MutedTest.kt new file mode 100644 index 00000000000..1d2934fe1d2 --- /dev/null +++ b/compiler/tests-mutes/src/org/jetbrains/kotlin/test/mutes/MutedTest.kt @@ -0,0 +1,93 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.test.mutes + +import java.io.File + +class MutedTest( + val key: String, + @Suppress("unused") val issue: String?, + val hasFailFile: Boolean, + val isFlaky: Boolean +) { + val methodKey: String + val classNameKey: String + val simpleClassName: String + + init { + val noQuoteKey = key.replace("`", "") + val beforeParamsKey = noQuoteKey.substringBefore("[") + val params = noQuoteKey.substringAfterWithDelimiter("[", "") + + methodKey = (beforeParamsKey.substringAfterLast(".", "") + params) + .also { + if (it.isEmpty()) throw IllegalArgumentException("Can't get method name: '$key'") + } + + classNameKey = beforeParamsKey.substringBeforeLast(".", "").also { + if (it.isEmpty()) throw IllegalArgumentException("Can't get class name: '$key'") + } + + simpleClassName = classNameKey.substringAfterLast(".") + } + + companion object { + fun String.substringAfterWithDelimiter(delimiter: String, missingDelimiterValue: String = this): String { + val index = indexOf(delimiter) + return if (index == -1) missingDelimiterValue else (delimiter + substring(index + 1, length)) + } + } +} + +fun getMutedTest(testClass: Class<*>, methodKey: String): MutedTest? { + return mutedSet.mutedTest(testClass, methodKey) +} + +internal fun loadMutedTests(file: File): List { + if (!file.exists()) { + System.err.println("Can't find mute file: ${file.absolutePath}") + return listOf() + } + + try { + val testLines = file.readLines() + .asSequence() + .map { it.trim() } + .filter { it.isNotEmpty() } + .toList() + + return testLines.drop(1).map { parseMutedTest(it) } + } catch (ex: Throwable) { + throw ParseError("Couldn't parse file with muted tests: $file", cause = ex) + } +} + +private val COLUMN_PARSE_REGEXP = Regex("\\s*(?:(?:\"((?:[^\"]|\"\")*)\")|([^,]*))\\s*") +private val MUTE_LINE_PARSE_REGEXP = Regex("$COLUMN_PARSE_REGEXP,$COLUMN_PARSE_REGEXP,$COLUMN_PARSE_REGEXP,$COLUMN_PARSE_REGEXP") +private fun parseMutedTest(str: String): MutedTest { + val matchResult = MUTE_LINE_PARSE_REGEXP.matchEntire(str) ?: throw ParseError("Can't parse the line: $str") + val resultValues = matchResult.groups.filterNotNull() + + val testKey = resultValues[1].value + val issue = resultValues[2].value + val stateStr = resultValues[3].value + val statusStr = resultValues[4].value + + val hasFailFile = when (stateStr) { + "MUTE", "" -> false + "FAIL" -> true + else -> throw ParseError("Invalid state (`$stateStr`), MUTE, FAIL or empty are expected: $str") + } + val isFlaky = when (statusStr) { + "FLAKY" -> true + "" -> false + else -> throw ParseError("Invalid status (`$statusStr`), FLAKY or empty are expected: $str") + } + + return MutedTest(testKey, issue, hasFailFile, isFlaky) +} + +private class ParseError(message: String, override val cause: Throwable? = null) : IllegalArgumentException(message) \ No newline at end of file