[Tests] Native tests runTest applies source transformers

This commit is contained in:
Evgeniy.Zhelenskiy
2021-12-03 18:38:16 +03:00
parent 9b196c738f
commit c27b647ce8
12 changed files with 104 additions and 16 deletions
@@ -208,6 +208,17 @@ object NewTestGeneratorImpl : TestGenerator(METHOD_GENERATORS) {
var first = true var first = true
val transformers = testClassModel.predefinedTransformers(false)
if (transformers.isNotEmpty()) {
p.println("public ${testClassModel.name}() {")
p.pushIndent()
transformers.forEach { (path, transformer) -> p.println("register(\"$path\", $transformer);") }
p.popIndent()
p.println("}")
first = false
}
for (methodModel in testMethods) { for (methodModel in testMethods) {
if (methodModel is RunTestMethodModel) continue // should also skip its imports if (methodModel is RunTestMethodModel) continue // should also skip its imports
if (!methodModel.shouldBeGenerated()) continue // should also skip its imports if (!methodModel.shouldBeGenerated()) continue // should also skip its imports
@@ -274,4 +285,9 @@ object NewTestGeneratorImpl : TestGenerator(METHOD_GENERATORS) {
} }
return false return false
} }
private fun TestClassModel.predefinedTransformers(recursive: Boolean): List<Pair<String, String>> =
methods.mapNotNull { method ->
(method as? TransformingTestMethodModel)?.takeIf { it.isNative }?.let { it.source.file.path to it.transformer }
} + if (recursive) innerTestClasses.flatMap { it.predefinedTransformers(recursive) } else listOf()
} }
@@ -10,7 +10,6 @@ import org.jetbrains.kotlin.generators.util.TestGeneratorUtil
import org.jetbrains.kotlin.generators.util.extractTagsFromDirectory import org.jetbrains.kotlin.generators.util.extractTagsFromDirectory
import org.jetbrains.kotlin.test.TargetBackend import org.jetbrains.kotlin.test.TargetBackend
import java.io.File import java.io.File
import java.util.*
import java.util.regex.Pattern import java.util.regex.Pattern
import kotlin.reflect.KClass import kotlin.reflect.KClass
@@ -96,7 +95,7 @@ class TestGroup(
pattern: String = if (extension == null) """^([^\.]+)$""" else "^(.+)\\.$extension\$", pattern: String = if (extension == null) """^([^\.]+)$""" else "^(.+)\\.$extension\$",
excludedPattern: String? = null, excludedPattern: String? = null,
testMethod: String = "doTest", testMethod: String = "doTest",
singleClass: Boolean = false, // if true then tests from subdirectories will be flatten to single class singleClass: Boolean = false, // if true then tests from subdirectories will be flattened to single class
testClassName: String? = null, // specific name for generated test class testClassName: String? = null, // specific name for generated test class
// which backend will be used in test. Specifying value may affect some test with // which backend will be used in test. Specifying value may affect some test with
// directives TARGET_BACKEND/DONT_TARGET_EXACT_BACKEND won't be generated // directives TARGET_BACKEND/DONT_TARGET_EXACT_BACKEND won't be generated
@@ -24,7 +24,13 @@ object TransformingTestMethodGenerator : MethodGenerator<TransformingTestMethodM
override fun generateBody(method: TransformingTestMethodModel, p: Printer) { override fun generateBody(method: TransformingTestMethodModel, p: Printer) {
with(method) { with(method) {
val filePath = KtTestUtil.getFilePath(source.file) + if (source.file.isDirectory) "/" else "" val filePath = KtTestUtil.getFilePath(source.file) + if (source.file.isDirectory) "/" else ""
p.println("${RunTestMethodModel.METHOD_NAME}(\"$filePath\", ${method.transformer});") p.println("${RunTestMethodModel.METHOD_NAME}(\"$filePath\"${if (isNative) "" else ", $transformer"});")
if (isNative) {
p.println("/*")
p.println(" There is a registered source transformer for the testcase:")
transformer.lines().forEach { p.println(" $it") }
p.println("*/")
}
} }
} }
} }
@@ -17,7 +17,7 @@ open class SimpleTestMethodModel(
val file: File, val file: File,
private val filenamePattern: Pattern, private val filenamePattern: Pattern,
checkFilenameStartsLowerCase: Boolean?, checkFilenameStartsLowerCase: Boolean?,
protected val targetBackend: TargetBackend, internal val targetBackend: TargetBackend,
private val skipIgnored: Boolean, private val skipIgnored: Boolean,
override val tags: List<String> override val tags: List<String>
) : MethodModel { ) : MethodModel {
@@ -5,6 +5,8 @@
package org.jetbrains.kotlin.generators.model package org.jetbrains.kotlin.generators.model
import org.jetbrains.kotlin.test.TargetBackend
abstract class TransformingTestMethodModel(val source: SimpleTestMethodModel, val transformer: String) : MethodModel { abstract class TransformingTestMethodModel(val source: SimpleTestMethodModel, val transformer: String) : MethodModel {
override val kind: MethodModel.Kind override val kind: MethodModel.Kind
get() = Kind get() = Kind
@@ -15,4 +17,9 @@ abstract class TransformingTestMethodModel(val source: SimpleTestMethodModel, va
get() = source.tags get() = source.tags
object Kind : MethodModel.Kind() object Kind : MethodModel.Kind()
internal val isNative
get() = source.targetBackend in listOf(TargetBackend.NATIVE, TargetBackend.ANY)
// Native tests load sources before runTest call if more than 1 test is called, so we need to register it before.
// Existing native tests specify target backend as ANY, setting it to NATIVE removes some previously generated tests.
} }
@@ -15644,6 +15644,10 @@ public class NativeExtBlackBoxTestGenerated extends AbstractNativeBlackBoxTest {
@TestDataPath("$PROJECT_ROOT") @TestDataPath("$PROJECT_ROOT")
@NativeBlackBoxTestCaseGroupProvider(ExtTestCaseGroupProvider.class) @NativeBlackBoxTestCaseGroupProvider(ExtTestCaseGroupProvider.class)
public class InlineClasses { public class InlineClasses {
public InlineClasses() {
register("compiler/testData/codegen/box/inlineClasses/boxResultInlineClassOfConstructorCall.kt", s -> s.replaceAll("OPTIONAL_JVM_INLINE_ANNOTATION", ""));
}
@Test @Test
public void testAllFilesPresentInInlineClasses() throws Exception { public void testAllFilesPresentInInlineClasses() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/inlineClasses"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.NATIVE, true); KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/inlineClasses"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.NATIVE, true);
@@ -15700,7 +15704,11 @@ public class NativeExtBlackBoxTestGenerated extends AbstractNativeBlackBoxTest {
@Test @Test
@TestMetadata("boxResultInlineClassOfConstructorCall.kt") @TestMetadata("boxResultInlineClassOfConstructorCall.kt")
public void testBoxResultInlineClassOfConstructorCall_valueClasses() throws Exception { public void testBoxResultInlineClassOfConstructorCall_valueClasses() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/boxResultInlineClassOfConstructorCall.kt", s -> s.replaceAll("OPTIONAL_JVM_INLINE_ANNOTATION", "")); runTest("compiler/testData/codegen/box/inlineClasses/boxResultInlineClassOfConstructorCall.kt");
/*
There is a registered source transformer for the testcase:
s -> s.replaceAll("OPTIONAL_JVM_INLINE_ANNOTATION", "")
*/
} }
@Test @Test
@@ -10,14 +10,25 @@ import org.jetbrains.kotlin.konan.blackboxtest.support.NativeBlackBoxTestSupport
import org.jetbrains.kotlin.konan.blackboxtest.support.TestRunProvider import org.jetbrains.kotlin.konan.blackboxtest.support.TestRunProvider
import org.jetbrains.kotlin.konan.blackboxtest.support.util.getAbsoluteFile import org.jetbrains.kotlin.konan.blackboxtest.support.util.getAbsoluteFile
import org.junit.jupiter.api.extension.ExtendWith import org.junit.jupiter.api.extension.ExtendWith
import java.io.File
@ExtendWith(NativeBlackBoxTestSupport::class) @ExtendWith(NativeBlackBoxTestSupport::class)
abstract class AbstractNativeBlackBoxTest { abstract class AbstractNativeBlackBoxTest {
internal lateinit var testRunProvider: TestRunProvider internal lateinit var testRunProvider: TestRunProvider
private val toBeRegistered = mutableListOf<Pair<File, List<(String) -> String>>>()
internal fun onRunProviderSet() {
for ((file, transformer) in toBeRegistered) {
testRunProvider.setProcessors(file, transformer)
}
}
@Suppress("UNUSED_PARAMETER") fun register(@TestDataFile testDataFilePath: String, sourceTransformers: List<(String) -> String>) =
@JvmOverloads toBeRegistered.add(File(testDataFilePath) to sourceTransformers)
fun runTest(@TestDataFile testDataFilePath: String, sourceTransformer: (String) -> String = { it }): Unit = with(testRunProvider) {
fun register(@TestDataFile testDataFilePath: String, sourceTransformer: (String) -> String) =
register(testDataFilePath, listOf(sourceTransformer))
fun runTest(@TestDataFile testDataFilePath: String): Unit = with(testRunProvider) {
val testDataFile = getAbsoluteFile(testDataFilePath) val testDataFile = getAbsoluteFile(testDataFilePath)
val testRun = getSingleTestRun(testDataFile) val testRun = getSingleTestRun(testDataFile)
val testRunner = createRunner(testRun) val testRunner = createRunner(testRun)
@@ -30,6 +30,7 @@ class NativeBlackBoxTestSupport : BeforeEachCallback {
*/ */
override fun beforeEach(extensionContext: ExtensionContext): Unit = with(extensionContext) { override fun beforeEach(extensionContext: ExtensionContext): Unit = with(extensionContext) {
enclosingTestInstance.testRunProvider = getOrCreateTestRunProvider() enclosingTestInstance.testRunProvider = getOrCreateTestRunProvider()
enclosingTestInstance.onRunProviderSet()
// Set the essential compiler property. // Set the essential compiler property.
System.setProperty("kotlin.native.home", getOrCreateGlobalEnvironment().kotlinNativeHome.path) System.setProperty("kotlin.native.home", getOrCreateGlobalEnvironment().kotlinNativeHome.path)
@@ -24,6 +24,10 @@ internal class TestRunProvider(
private val compilationFactory = TestCompilationFactory(environment) private val compilationFactory = TestCompilationFactory(environment)
private val cachedCompilations = ThreadSafeCache<TestCompilationCacheKey, TestCompilation>() private val cachedCompilations = ThreadSafeCache<TestCompilationCacheKey, TestCompilation>()
fun setProcessors(testDataFile: File, sourceTransformers: List<(String) -> String>) {
testCaseGroupProvider.setPreprocessors(testDataFile, sourceTransformers)
}
fun getSingleTestRun(testDataFile: File): TestRun { fun getSingleTestRun(testDataFile: File): TestRun {
environment.assertNotDisposed() environment.assertNotDisposed()
@@ -38,6 +38,7 @@ import java.io.File
internal class ExtTestCaseGroupProvider( internal class ExtTestCaseGroupProvider(
private val environment: TestEnvironment private val environment: TestEnvironment
) : TestCaseGroupProvider, TestDisposable(parentDisposable = environment) { ) : TestCaseGroupProvider, TestDisposable(parentDisposable = environment) {
private val sourceTransformers: MutableMap<String, List<(String) -> String>> = mutableMapOf()
private val structureFactory = ExtTestDataFileStructureFactory(parentDisposable = this) private val structureFactory = ExtTestDataFileStructureFactory(parentDisposable = this)
private val sharedModules = ThreadSafeCache<String, TestModule.Shared?>() private val sharedModules = ThreadSafeCache<String, TestModule.Shared?>()
@@ -55,7 +56,9 @@ internal class ExtTestCaseGroupProvider(
val testCases = mutableListOf<TestCase>() val testCases = mutableListOf<TestCase>()
testDataFiles.forEach { testDataFile -> testDataFiles.forEach { testDataFile ->
val extTestDataFile = ExtTestDataFile(environment, structureFactory, testDataFile) val extTestDataFile = ExtTestDataFile(
environment, structureFactory, testDataFile, sourceTransformers[testDataFile.canonicalPath] ?: listOf()
)
if (extTestDataFile.isRelevant) if (extTestDataFile.isRelevant)
testCases += extTestDataFile.createTestCase( testCases += extTestDataFile.createTestCase(
@@ -69,6 +72,13 @@ internal class ExtTestCaseGroupProvider(
TestCaseGroup.Default(disabledTestDataFileNames, testCases) TestCaseGroup.Default(disabledTestDataFileNames, testCases)
} }
override fun setPreprocessors(testDataDir: File, preprocessors: List<(String) -> String>) {
if (preprocessors.isNotEmpty())
sourceTransformers[testDataDir.canonicalPath] = preprocessors
else
sourceTransformers.remove(testDataDir.canonicalPath)
}
override fun getTestCaseGroup(testDataDir: File): TestCaseGroup? { override fun getTestCaseGroup(testDataDir: File): TestCaseGroup? {
assertNotDisposed() assertNotDisposed()
return lazyTestCaseGroups[testDataDir] return lazyTestCaseGroups[testDataDir]
@@ -117,10 +127,11 @@ internal class ExtTestCaseGroupProvider(
private class ExtTestDataFile( private class ExtTestDataFile(
private val environment: TestEnvironment, private val environment: TestEnvironment,
structureFactory: ExtTestDataFileStructureFactory, structureFactory: ExtTestDataFileStructureFactory,
private val testDataFile: File private val testDataFile: File,
sourceTransformers: List<(String) -> String>
) { ) {
private val structure by lazy { private val structure by lazy {
structureFactory.ExtTestDataFileStructure(testDataFile) { line -> structureFactory.ExtTestDataFileStructure(testDataFile, sourceTransformers) { line ->
// Remove all diagnostic parameters from the text. Examples: // Remove all diagnostic parameters from the text. Examples:
// <!NO_TAIL_CALLS_FOUND!>, <!NON_TAIL_RECURSIVE_CALL!>, <!>. // <!NO_TAIL_CALLS_FOUND!>, <!NON_TAIL_RECURSIVE_CALL!>, <!>.
line.replace(DIAGNOSTIC_REGEX) { match -> match.groupValues[1] } line.replace(DIAGNOSTIC_REGEX) { match -> match.groupValues[1] }
@@ -642,12 +653,16 @@ private typealias SharedModuleCache = (moduleName: String, generator: SharedModu
private class ExtTestDataFileStructureFactory(parentDisposable: Disposable) : TestDisposable(parentDisposable) { private class ExtTestDataFileStructureFactory(parentDisposable: Disposable) : TestDisposable(parentDisposable) {
private val psiFactory = createPsiFactory(parentDisposable = this) private val psiFactory = createPsiFactory(parentDisposable = this)
inner class ExtTestDataFileStructure(originalTestDataFile: File, initialCleanUpTransformation: (String) -> String) { inner class ExtTestDataFileStructure(
originalTestDataFile: File,
sourceTransformers: List<(String) -> String>,
initialCleanUpTransformation: (String) -> String
) {
init { init {
assertNotDisposed() assertNotDisposed()
} }
private val filesAndModules = FilesAndModules(originalTestDataFile, initialCleanUpTransformation) private val filesAndModules = FilesAndModules(originalTestDataFile, initialCleanUpTransformation, sourceTransformers)
val directives: Directives get() = filesAndModules.directives val directives: Directives get() = filesAndModules.directives
@@ -803,12 +818,16 @@ private class ExtTestDataFileStructureFactory(parentDisposable: Disposable) : Te
ExtTestModule(name, dependencies, friends) ExtTestModule(name, dependencies, friends)
} }
private inner class FilesAndModules(originalTestDataFile: File, initialCleanUpTransformation: (String) -> String) { private inner class FilesAndModules(
originalTestDataFile: File,
initialCleanUpTransformation: (String) -> String,
sourceTransformers: List<(String) -> String>
) {
private val testFileFactory = ExtTestFileFactory() private val testFileFactory = ExtTestFileFactory()
private val generatedFiles = TestFiles.createTestFiles( private val generatedFiles = TestFiles.createTestFiles(
/* testFileName = */ DEFAULT_FILE_NAME, /* testFileName = */ DEFAULT_FILE_NAME,
/* expectedText = */ originalTestDataFile.readText(), /* expectedText = */ originalTestDataFile.applySourceTransformers(sourceTransformers),
/* factory = */ testFileFactory, /* factory = */ testFileFactory,
/* preserveLocations = */ true /* preserveLocations = */ true
) )
@@ -21,6 +21,8 @@ import org.jetbrains.kotlin.test.services.impl.RegisteredDirectivesParser
import java.io.File import java.io.File
internal class StandardTestCaseGroupProvider(private val environment: TestEnvironment) : TestCaseGroupProvider { internal class StandardTestCaseGroupProvider(private val environment: TestEnvironment) : TestCaseGroupProvider {
val sourceTransformers: MutableMap<String, List<(String) -> String>> = mutableMapOf()
// Load test cases in groups on demand. // Load test cases in groups on demand.
private val lazyTestCaseGroups = ThreadSafeFactory<File, TestCaseGroup?> { testDataDir -> private val lazyTestCaseGroups = ThreadSafeFactory<File, TestCaseGroup?> { testDataDir ->
val testDataFiles = testDataDir.listFiles() val testDataFiles = testDataDir.listFiles()
@@ -36,6 +38,13 @@ internal class StandardTestCaseGroupProvider(private val environment: TestEnviro
TestCaseGroup.Default(disabledTestDataFileNames = emptySet(), testCases = testCases) TestCaseGroup.Default(disabledTestDataFileNames = emptySet(), testCases = testCases)
} }
override fun setPreprocessors(testDataDir: File, preprocessors: List<(String) -> String>) {
if (preprocessors.isNotEmpty())
sourceTransformers[testDataDir.canonicalPath] = preprocessors
else
sourceTransformers.remove(testDataDir.canonicalPath)
}
override fun getTestCaseGroup(testDataDir: File) = lazyTestCaseGroups[testDataDir] override fun getTestCaseGroup(testDataDir: File) = lazyTestCaseGroups[testDataDir]
private fun createTestCase(testDataFile: File): TestCase { private fun createTestCase(testDataFile: File): TestCase {
@@ -100,7 +109,8 @@ internal class StandardTestCaseGroupProvider(private val environment: TestEnviro
} }
} }
testDataFile.readLines().forEachIndexed { lineNumber, line -> val text = testDataFile.applySourceTransformers(sourceTransformers[testDataFile.canonicalPath] ?: listOf())
text.lines().forEachIndexed { lineNumber, line ->
val location = Location(testDataFile, lineNumber) val location = Location(testDataFile, lineNumber)
val expectFileDirectiveAfterModuleDirective = val expectFileDirectiveAfterModuleDirective =
lastParsedDirective == TestDirectives.MODULE // Only FILE directive may follow MODULE directive. lastParsedDirective == TestDirectives.MODULE // Only FILE directive may follow MODULE directive.
@@ -9,5 +9,12 @@ import org.jetbrains.kotlin.konan.blackboxtest.support.TestCaseGroup
import java.io.File import java.io.File
internal interface TestCaseGroupProvider { internal interface TestCaseGroupProvider {
fun setPreprocessors(testDataDir: File, preprocessors: List<(String) -> String>)
fun getTestCaseGroup(testDataDir: File): TestCaseGroup? fun getTestCaseGroup(testDataDir: File): TestCaseGroup?
} }
internal fun String.applySourceTransformers(sourceTransformers: List<(String) -> String>) =
sourceTransformers.fold(this) { source, transformer -> transformer(source) }
internal fun File.applySourceTransformers(sourceTransformers: List<(String) -> String>) =
readText().applySourceTransformers(sourceTransformers)