[Tests] Introduce replacing source transformer

This commit is contained in:
Evgeniy.Zhelenskiy
2021-12-07 02:48:30 +03:00
parent 38ff3f5a24
commit cb4ec932d7
18 changed files with 80 additions and 42 deletions
@@ -29,7 +29,7 @@ import org.jetbrains.kotlin.test.model.FrontendKinds
import org.jetbrains.kotlin.test.model.ResultingArtifact
import org.jetbrains.kotlin.test.model.TestFile
import org.jetbrains.kotlin.test.runners.AbstractKotlinCompilerTest
import org.jetbrains.kotlin.test.runners.TransformersFunctions.Android
import org.jetbrains.kotlin.test.utils.TransformersFunctions.Android
import org.jetbrains.kotlin.test.services.*
import org.jetbrains.kotlin.test.services.configuration.CommonEnvironmentConfigurator
import org.jetbrains.kotlin.test.services.configuration.JvmEnvironmentConfigurator
@@ -8,7 +8,7 @@ package org.jetbrains.kotlin.test.runners.codegen;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.util.KtTestUtil;
import org.jetbrains.kotlin.test.TargetBackend;
import org.jetbrains.kotlin.test.runners.TransformersFunctions;
import org.jetbrains.kotlin.test.utils.TransformersFunctions;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
@@ -51,7 +51,7 @@ class GlobalMetadataInfoHandler(
infos += codeMetaInfos
}
fun compareAllMetaDataInfos(expectedTransformer: ((String) -> String)?) {
fun compareAllMetaDataInfos() {
// TODO: adapt to multiple testdata files
val moduleStructure = testServices.moduleStructure
val builder = StringBuilder()
@@ -66,18 +66,15 @@ class GlobalMetadataInfoHandler(
codeMetaInfos,
testServices.sourceFileProvider.getContentOfSourceFile(file)
)
builder.append(fileBuilder.stripAdditionalEmptyLines(file))
val reverseTransformers = testServices.sourceFileProvider.preprocessors.filterIsInstance<ReversibleSourceFilePreprocessor>()
val initialFileContent = fileBuilder.stripAdditionalEmptyLines(file).toString()
val actualFileContent =
reverseTransformers.foldRight(initialFileContent) { transformer, source -> transformer.revert(file, source) }
builder.append(actualFileContent)
}
}
val actualText = builder.toString()
val expectedFile = moduleStructure.originalTestDataFiles.single()
if (expectedTransformer != null) {
val expectedContent = expectedFile.readText().let(expectedTransformer)
val message = "Actual data differs from transformed content of file $expectedFile"
testServices.assertions.assertEquals(expectedContent, actualText) { message }
} else {
testServices.assertions.assertEqualsToFile(expectedFile, actualText)
}
testServices.assertions.assertEqualsToFile(moduleStructure.originalTestDataFiles.single(), actualText)
}
private fun StringBuilder.stripAdditionalEmptyLines(file: TestFile): CharSequence {
@@ -1,6 +1,6 @@
// WITH_STDLIB
// WORKS_WHEN_VALUE_CLASS
// !LANGUAGE: +ValueClasses
// LANGUAGE: +ValueClasses
OPTIONAL_JVM_INLINE_ANNOTATION
value class Result<T>(val a: Any?)
@@ -8,7 +8,7 @@ package org.jetbrains.kotlin.test.runners.codegen;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.util.KtTestUtil;
import org.jetbrains.kotlin.test.TargetBackend;
import org.jetbrains.kotlin.test.runners.TransformersFunctions;
import org.jetbrains.kotlin.test.utils.TransformersFunctions;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
@@ -8,7 +8,7 @@ package org.jetbrains.kotlin.test.runners.codegen;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.util.KtTestUtil;
import org.jetbrains.kotlin.test.TargetBackend;
import org.jetbrains.kotlin.test.runners.TransformersFunctions;
import org.jetbrains.kotlin.test.utils.TransformersFunctions;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.test.model.TestFile
import org.jetbrains.kotlin.test.preprocessors.MetaInfosCleanupPreprocessor
import org.jetbrains.kotlin.test.services.*
import org.jetbrains.kotlin.test.services.impl.TemporaryDirectoryManagerImpl
import org.jetbrains.kotlin.test.utils.ReplacingSourceTransformer
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.types.FlexibleTypeImpl
import org.junit.jupiter.api.BeforeEach
@@ -81,18 +82,17 @@ abstract class AbstractKotlinCompilerTest {
testRunner(filePath, configuration).runTest(filePath)
}
@JvmOverloads
open fun runTest(
@TestDataFile filePath: String,
actualContentModifier: (String) -> String,
expectedContentModifier: ((String) -> String)? = actualContentModifier
contentModifier: ReplacingSourceTransformer,
) {
class SourceTransformer(testServices: TestServices) : SourceFilePreprocessor(testServices) {
override fun process(file: TestFile, content: String): String = content.let(actualContentModifier)
class SourceTransformer(testServices: TestServices) : ReversibleSourceFilePreprocessor(testServices) {
override fun process(file: TestFile, content: String): String = contentModifier.invokeForTestFile(file, content)
override fun revert(file: TestFile, actualContent: String): String = contentModifier.revertForFile(file, actualContent)
}
testRunner(filePath) {
configuration.let { it() } // property configuration, not method
useSourcePreprocessor({ SourceTransformer(it) })
}.runTest(filePath, expectedFileTransformer = expectedContentModifier)
configuration.invoke(this)
useSourcePreprocessor(::SourceTransformer)
}.runTest(filePath)
}
}
@@ -0,0 +1,33 @@
/*
* Copyright 2010-2021 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.utils
import org.jetbrains.kotlin.test.model.TestFile
import java.util.function.Function
class ReplacingSourceTransformer(val from: String, val to: String) : Function<String, String>, (String) -> String {
init {
require(from.isNotEmpty()) { "Cannot replace empty string" }
require(from.lines().size == 1) { "Multiline text cannot be replaced yet" }
require(to.lines().size == from.lines().size) { "Number of lines cannot change" }
}
private val replacements: MutableMap<String?, List<List<String>>> = mutableMapOf()
fun invokeForTestFile(testFile: TestFile?, source: String): String {
val value = source.lines().map { it.split(from) }
replacements[testFile?.relativePath] = value
return value.joinToString("\n") { it.joinToString(to) }
}
fun revertForFile(testFile: TestFile?, actual: String): String = actual.lines().mapIndexed { index, line ->
val partition = replacements[testFile?.relativePath]?.elementAtOrNull(index) ?: return@mapIndexed line
if (partition.joinToString(to) == line) partition.joinToString(from) else line
}.joinToString("\n")
override fun apply(source: String): String = invokeForTestFile(null, source)
override fun invoke(source: String): String = invokeForTestFile(null, source)
}
@@ -3,7 +3,7 @@
* 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.runners
package org.jetbrains.kotlin.test.utils
import java.io.File
@@ -126,7 +126,7 @@ private class TestGeneratorImplInstance(
for (clazz in testClassModels.flatMapTo(mutableSetOf()) { classModel -> classModel.imports }) {
val realName = when (clazz) {
TransformingTestMethodModel.TransformerFunctionsClassPlaceHolder::class.java ->
"org.jetbrains.kotlin.test.runners.TransformersFunctions"
"org.jetbrains.kotlin.test.utils.TransformersFunctions"
else -> clazz.name
}
p.println("import $realName;")
@@ -10,7 +10,7 @@ import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
import org.jetbrains.kotlin.test.KotlinTestUtils;
import org.jetbrains.kotlin.test.util.KtTestUtil;
import org.jetbrains.kotlin.test.TargetBackend;
import org.jetbrains.kotlin.test.runners.TransformersFunctions;
import org.jetbrains.kotlin.test.utils.TransformersFunctions;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.runner.RunWith;
@@ -131,7 +131,7 @@ object NewTestGeneratorImpl : TestGenerator(METHOD_GENERATORS) {
for (clazz in testClassModels.flatMapTo(mutableSetOf()) { classModel -> classModel.imports }) {
val realName = when (clazz) {
TransformingTestMethodModel.TransformerFunctionsClassPlaceHolder::class.java ->
"org.jetbrains.kotlin.test.runners.TransformersFunctions"
"org.jetbrains.kotlin.test.utils.TransformersFunctions"
else -> clazz.name
}
p.println("import $realName;")
@@ -23,14 +23,21 @@ object TransformingTestMethodGenerator : MethodGenerator<TransformingTestMethodM
override fun generateBody(method: TransformingTestMethodModel, p: Printer) {
with(method) {
if (registerInConstructor) {
val lines = transformer.lines()
val message = "There is a registered source transformer for the testcase"
if (lines.size > 1) {
p.println("/*")
p.println(" $message:")
lines.forEach { p.println(" $it") }
p.println("*/")
} else {
val restOfLine = lines.firstOrNull()?.takeIf { it.isNotBlank() }?.let { ": $it" } ?: ""
p.println("// $message$restOfLine")
}
}
val filePath = KtTestUtil.getFilePath(source.file) + if (source.file.isDirectory) "/" else ""
p.println("${RunTestMethodModel.METHOD_NAME}(\"$filePath\"${if (registerInConstructor) "" else ", $transformer"});")
if (registerInConstructor) {
p.println("/*")
p.println(" There is a registered source transformer for the testcase:")
transformer.lines().forEach { p.println(" $it") }
p.println("*/")
}
}
}
}
@@ -10,9 +10,13 @@ import org.jetbrains.kotlin.test.TargetBackend
import java.io.File
import java.util.regex.Pattern
const val WORKS_WHEN_VALUE_CLASS = "WORKS_WHEN_VALUE_CLASS"
// will replace OPTIONAL_JVM_INLINE_ANNOTATION with @JvmInline or remove it depending on compiler backend
// for JVM IR both ones are generated according to value classes feature (https://github.com/Kotlin/KEEP/issues/237)
fun TestEntityModel.containsWithoutJvmInline(): Boolean = when (this) {
is ClassModel -> methods.any { it.containsWithoutJvmInline() } || innerTestClasses.any { it.containsWithoutJvmInline() }
is SimpleTestMethodModel -> file.isFile && file.readLines().any { Regex("^\\s*//\\s*WORKS_WHEN_VALUE_CLASS\\s*$").matches(it) }
is SimpleTestMethodModel -> file.isFile && file.readLines().any { Regex("^\\s*//\\s*$WORKS_WHEN_VALUE_CLASS\\s*$").matches(it) }
else -> false
}
@@ -8,7 +8,7 @@ package org.jetbrains.kotlin.js.test;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.util.KtTestUtil;
import org.jetbrains.kotlin.test.TargetBackend;
import org.jetbrains.kotlin.test.runners.TransformersFunctions;
import org.jetbrains.kotlin.test.utils.TransformersFunctions;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
@@ -8,7 +8,7 @@ package org.jetbrains.kotlin.js.test.ir;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.util.KtTestUtil;
import org.jetbrains.kotlin.test.TargetBackend;
import org.jetbrains.kotlin.test.runners.TransformersFunctions;
import org.jetbrains.kotlin.test.utils.TransformersFunctions;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
@@ -10,7 +10,7 @@ import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
import org.jetbrains.kotlin.test.KotlinTestUtils;
import org.jetbrains.kotlin.test.util.KtTestUtil;
import org.jetbrains.kotlin.test.TargetBackend;
import org.jetbrains.kotlin.test.runners.TransformersFunctions;
import org.jetbrains.kotlin.test.utils.TransformersFunctions;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.runner.RunWith;
@@ -10,7 +10,7 @@ import org.jetbrains.kotlin.test.util.KtTestUtil;
import org.jetbrains.kotlin.konan.blackboxtest.support.group.NativeBlackBoxTestCaseGroupProvider;
import org.jetbrains.kotlin.konan.blackboxtest.support.group.ExtTestCaseGroupProvider;
import org.jetbrains.kotlin.test.TargetBackend;
import org.jetbrains.kotlin.test.runners.TransformersFunctions;
import org.jetbrains.kotlin.test.utils.TransformersFunctions;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
@@ -15705,11 +15705,8 @@ public class NativeExtBlackBoxTestGenerated extends AbstractNativeBlackBoxTest {
@Test
@TestMetadata("boxResultInlineClassOfConstructorCall.kt")
public void testBoxResultInlineClassOfConstructorCall() throws Exception {
// There is a registered source transformer for the testcase: TransformersFunctions::removeOptionalJvmInlineAnnotation
runTest("compiler/testData/codegen/box/inlineClasses/boxResultInlineClassOfConstructorCall.kt");
/*
There is a registered source transformer for the testcase:
TransformersFunctions::removeOptionalJvmInlineAnnotation
*/
}
@Test