Allow to mute JUnit 4 tests with parameters

This commit is contained in:
Nikolay Krasko
2019-12-20 02:24:42 +03:00
parent d74721716a
commit 8014a6509e
@@ -12,7 +12,6 @@ import org.junit.runners.parameterized.BlockJUnit4ClassRunnerWithParameters
import org.junit.runners.parameterized.ParametersRunnerFactory import org.junit.runners.parameterized.ParametersRunnerFactory
import org.junit.runners.parameterized.TestWithParameters import org.junit.runners.parameterized.TestWithParameters
import java.io.File import java.io.File
import java.lang.reflect.Method
private class AutoMute( private class AutoMute(
val file: String, val file: String,
@@ -37,24 +36,44 @@ private class MutedTest(
@Suppress("unused") val issue: String?, @Suppress("unused") val issue: String?,
val hasFailFile: Boolean val hasFailFile: Boolean
) { ) {
val methodName = key.substringAfterLast(".", "").replace("`", "").also { val methodKey: String
if (it.isEmpty()) throw IllegalArgumentException("Can't get method name") 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")
}
classNameKey = beforeParamsKey.substringBeforeLast(".", "").also {
if (it.isEmpty()) throw IllegalArgumentException("Can't get class name")
}
simpleClassName = classNameKey.substringAfterLast(".")
} }
val classNameKey = key.substringBeforeLast(".", "").also {
if (it.isEmpty()) throw IllegalArgumentException("Can't get class name") 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))
}
} }
val simpleClassName = classNameKey.substringAfterLast(".")
} }
private class MutedSet(muted: List<MutedTest>) { private class MutedSet(muted: List<MutedTest>) {
// Method name -> Simple class name -> List of muted tests // Method key -> Simple class name -> List of muted tests
private val cache: Map<String, Map<String, List<MutedTest>>> = private val cache: Map<String, Map<String, List<MutedTest>>> =
muted muted
.groupBy { it.methodName } // Method name -> List of muted tests .groupBy { it.methodKey } // Method key -> List of muted tests
.mapValues { (_, tests) -> tests.groupBy { it.simpleClassName } } .mapValues { (_, tests) -> tests.groupBy { it.simpleClassName } }
fun mutedTest(testClass: Class<*>, methodName: String): MutedTest? { fun mutedTest(testClass: Class<*>, methodKey: String): MutedTest? {
val mutedTests = cache[methodName]?.get(testClass.simpleName) ?: return null val mutedTests = cache[methodKey]?.get(testClass.simpleName) ?: return null
return mutedTests.firstOrNull { mutedTest -> return mutedTests.firstOrNull { mutedTest ->
testClass.canonicalName.endsWith(mutedTest.classNameKey) testClass.canonicalName.endsWith(mutedTest.classNameKey)
@@ -85,14 +104,21 @@ private fun loadMutedTests(file: File): List<MutedTest> {
} }
} }
private val MUTE_LINE_PARSE_REGEXP = Regex("^([^,\\[]+)(\\[[^]]+])?(,\\s*)?([^,]+)?(,\\s*)?([^,]+)?$")
private fun parseMutedTest(str: String): MutedTest { private fun parseMutedTest(str: String): MutedTest {
val components = str.splitToSequence(",").map { it.trim() }.toList() val matchResult = MUTE_LINE_PARSE_REGEXP.matchEntire(str) ?: throw ParseError("Can't parse the line: $str")
val key = components.getOrNull(0) ?: throw ParseError("Test key is absent (1 column): $str") val methodFull = matchResult.groups[1]?.value ?: throw ParseError("Test key is absent (1 column): $str")
val params = matchResult.groups[2]?.value ?: ""
val issue = components.getOrNull(1) val key = methodFull + params
val hasFailFile = when (val stateStr = components.getOrNull(2)) { val issue = matchResult.groups[4]?.value
val stateStr = matchResult.groups[6]?.value
val hasFailFile = when (stateStr) {
"MUTE", "", null -> false "MUTE", "", null -> false
"FAIL" -> true "FAIL" -> true
else -> throw ParseError("Invalid state (`$stateStr`), MUTE, FAIL or empty are expected: $str") else -> throw ParseError("Invalid state (`$stateStr`), MUTE, FAIL or empty are expected: $str")
@@ -115,12 +141,8 @@ internal fun isMutedInDatabase(testCase: TestCase): Boolean {
return isMutedInDatabase(testCase.javaClass, testCase.name) return isMutedInDatabase(testCase.javaClass, testCase.name)
} }
fun isMutedInDatabase(method: Method): Boolean { fun isMutedInDatabase(testClass: Class<*>, methodKey: String): Boolean {
return isMutedInDatabase(method.declaringClass, method.name) val mutedTest = mutedSet.mutedTest(testClass, methodKey)
}
fun isMutedInDatabase(testClass: Class<*>, methodName: String): Boolean {
val mutedTest = mutedSet.mutedTest(testClass, methodName)
return mutedTest != null && !mutedTest.hasFailFile return mutedTest != null && !mutedTest.hasFailFile
} }
@@ -152,22 +174,28 @@ internal fun wrapWithMuteInDatabase(testCase: TestCase, f: () -> Unit): (() -> U
private fun mutedMessage(key: String) = "MUTED TEST: $key" private fun mutedMessage(key: String) = "MUTED TEST: $key"
private fun testKey(testCase: TestCase) = "${testCase::class.java.canonicalName}.${testCase.name}" private fun testKey(klass: Class<*>, methodKey: String) = "${klass.canonicalName}.$methodKey"
private fun testKey(method: Method) = "${method.declaringClass.canonicalName}.${method.name}" private fun testKey(testCase: TestCase) = testKey(testCase::class.java, testCase.name)
class RunnerFactoryWithMuteInDatabase: ParametersRunnerFactory { class RunnerFactoryWithMuteInDatabase: ParametersRunnerFactory {
override fun createRunnerForTestWithParameters(test: TestWithParameters?): Runner { override fun createRunnerForTestWithParameters(test: TestWithParameters?): Runner {
return object : BlockJUnit4ClassRunnerWithParameters(test) { return object : BlockJUnit4ClassRunnerWithParameters(test) {
override fun isIgnored(child: FrameworkMethod): Boolean { override fun isIgnored(child: FrameworkMethod): Boolean {
return super.isIgnored(child) || isIgnoredInDatabaseWithLog(child) return super.isIgnored(child) || isIgnoredInDatabaseWithLog(child, name)
} }
} }
} }
} }
fun isIgnoredInDatabaseWithLog(child: FrameworkMethod): Boolean { fun isIgnoredInDatabaseWithLog(child: FrameworkMethod, parametersName: String): Boolean {
if (isMutedInDatabase(child.method)) { if (isMutedInDatabase(child.declaringClass, child.name)) {
System.err.println(mutedMessage(testKey(child.method))) System.err.println(mutedMessage(testKey(child.declaringClass, child.name)))
return true
}
val methodWithParametersKey = child.method.name + parametersName
if (isMutedInDatabase(child.declaringClass, methodWithParametersKey)) {
System.err.println(mutedMessage(testKey(child.declaringClass, methodWithParametersKey)))
return true return true
} }