[Test] Migrate AbstractBlackBoxCodegenTest to new infrastructure

This commit is contained in:
Dmitriy Novozhilov
2020-12-25 12:41:20 +03:00
committed by TeamCityServer
parent f01122d8dc
commit 85c87f7df9
47 changed files with 7856 additions and 2932 deletions
@@ -14,6 +14,7 @@ dependencies {
testCompileOnly(project(":kotlin-reflect-api"))
testRuntimeOnly(project(":kotlin-reflect"))
testRuntimeOnly(project(":core:descriptors.runtime"))
testRuntimeOnly(androidDxJar())
testImplementation(projectTests(":generators:test-generator"))
@@ -9,6 +9,8 @@ import org.jetbrains.kotlin.codegen.ClassBuilderFactories
import org.jetbrains.kotlin.codegen.DefaultCodegenFactory
import org.jetbrains.kotlin.codegen.KotlinCodegenFacade
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.test.directives.CodegenTestDirectives
import org.jetbrains.kotlin.test.directives.model.DirectivesContainer
import org.jetbrains.kotlin.test.model.ArtifactKinds
import org.jetbrains.kotlin.test.model.BinaryArtifacts
import org.jetbrains.kotlin.test.model.TestModule
@@ -18,12 +20,17 @@ import org.jetbrains.kotlin.test.services.compilerConfigurationProvider
class ClassicJvmBackendFacade(
testServices: TestServices
) : ClassicBackendFacade<BinaryArtifacts.Jvm>(testServices, ArtifactKinds.Jvm) {
private val javaCompilerFacade = JavaCompilerFacade(testServices)
override val additionalDirectives: List<DirectivesContainer>
get() = listOf(CodegenTestDirectives)
override fun transform(
module: TestModule,
inputArtifact: ClassicBackendInput
): BinaryArtifacts.Jvm {
val compilerConfiguration = testServices.compilerConfigurationProvider.getCompilerConfiguration(module)
val (psiFiles, bindingContext, moduleDescriptor, project, languageVersionSettings) = inputArtifact
val configuration = testServices.compilerConfigurationProvider.getCompilerConfiguration(module)
val (psiFiles, bindingContext, moduleDescriptor, project, _) = inputArtifact
// TODO: add configuring classBuilderFactory
val generationState = GenerationState.Builder(
project,
@@ -31,11 +38,11 @@ class ClassicJvmBackendFacade(
moduleDescriptor,
bindingContext,
psiFiles.toList(),
compilerConfiguration
configuration
).codegenFactory(DefaultCodegenFactory).build()
KotlinCodegenFacade.compileCorrectFiles(generationState)
javaCompilerFacade.compileJavaFiles(module, configuration, generationState.factory)
return BinaryArtifacts.Jvm(generationState.factory)
}
}
@@ -0,0 +1,91 @@
/*
* 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.backend.classic
import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots
import org.jetbrains.kotlin.codegen.ClassFileFactory
import org.jetbrains.kotlin.codegen.CodegenTestUtil
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.config.JvmTarget
import org.jetbrains.kotlin.test.compileJavaFilesExternally
import org.jetbrains.kotlin.test.directives.CodegenTestDirectives
import org.jetbrains.kotlin.test.directives.JvmEnvironmentConfigurationDirectives
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.services.assertions
import org.jetbrains.kotlin.test.services.javaFiles
import org.jetbrains.kotlin.test.services.jvm.compiledClassesManager
import org.jetbrains.kotlin.test.services.sourceFileProvider
import org.jetbrains.kotlin.test.util.KtTestUtil
import java.io.File
import java.nio.file.Paths
class JavaCompilerFacade(private val testServices: TestServices) {
@OptIn(ExperimentalStdlibApi::class)
fun compileJavaFiles(module: TestModule, configuration: CompilerConfiguration, classFileFactory: ClassFileFactory) {
if (module.javaFiles.isEmpty()) return
val javaClasspath = buildList {
add(testServices.compiledClassesManager.getCompiledKotlinDirForModule(module, classFileFactory).path)
addAll(configuration.jvmClasspathRoots.map { it.absolutePath })
if (JvmEnvironmentConfigurationDirectives.ANDROID_ANNOTATIONS in module.directives) {
add(ForTestCompileRuntime.androidAnnotationsForTests().path)
}
}
val javaClassesOutputDirectory = testServices.compiledClassesManager.getOrCreateCompiledJavaDirForModule(module)
val javacOptions = extractJavacOptions(
module,
configuration[JVMConfigurationKeys.JVM_TARGET],
configuration.getBoolean(JVMConfigurationKeys.ENABLE_JVM_PREVIEW)
)
val finalJavacOptions = CodegenTestUtil.prepareJavacOptions(
javaClasspath,
javacOptions,
javaClassesOutputDirectory
)
val javaFiles = module.javaFiles.map { testServices.sourceFileProvider.getRealFileForSourceFile(it) }
compileJavaFiles(configuration[JVMConfigurationKeys.JVM_TARGET] ?: JvmTarget.DEFAULT, javaFiles, finalJavacOptions)
}
@OptIn(ExperimentalStdlibApi::class)
private fun extractJavacOptions(module: TestModule, kotlinTarget: JvmTarget?, isJvmPreviewEnabled: Boolean): List<String> {
return buildList {
addAll(module.directives[CodegenTestDirectives.JAVAC_OPTIONS])
if (kotlinTarget != null && isJvmPreviewEnabled) {
add("--release")
add(kotlinTarget.description)
add("--enable-preview")
return@buildList
}
CodegenTestUtil.computeJavaTarget(this, kotlinTarget)?.let { javaTarget ->
add("-source")
add(javaTarget)
add("-target")
add(javaTarget)
}
}
}
private fun compileJavaFiles(jvmTarget: JvmTarget, files: List<File>, javacOptions: List<String>) {
val targetIsJava8OrLower = System.getProperty("java.version").startsWith("1.")
if (targetIsJava8OrLower) {
org.jetbrains.kotlin.test.compileJavaFiles(files, javacOptions, assertions = testServices.assertions)
return
}
val jdkHome = when (jvmTarget) {
JvmTarget.JVM_9 -> KtTestUtil.getJdk9Home()
JvmTarget.JVM_11 -> KtTestUtil.getJdk11Home()
JvmTarget.JVM_15 -> KtTestUtil.getJdk15Home()
else -> null
} ?: error("JDK for $jvmTarget is not found")
compileJavaFilesExternally(files, javacOptions, jdkHome)
}
}
@@ -0,0 +1,43 @@
/*
* 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.backend.handlers
import org.jetbrains.kotlin.codegen.BytecodeListingTextCollectingVisitor
import org.jetbrains.kotlin.test.directives.CodegenTestDirectives
import org.jetbrains.kotlin.test.directives.CodegenTestDirectives.CHECK_BYTECODE_LISTING
import org.jetbrains.kotlin.test.directives.model.DirectivesContainer
import org.jetbrains.kotlin.test.model.BinaryArtifacts
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.services.defaultsProvider
import org.jetbrains.kotlin.test.services.moduleStructure
import org.jetbrains.kotlin.test.utils.MultiModuleInfoDumperImpl
import org.jetbrains.kotlin.test.utils.withSuffixAndExtension
class BytecodeListingHandler(testServices: TestServices) : JvmBinaryArtifactHandler(testServices) {
override val directivesContainers: List<DirectivesContainer>
get() = listOf(CodegenTestDirectives)
private val multiModuleInfoDumper = MultiModuleInfoDumperImpl()
override fun processModule(module: TestModule, info: BinaryArtifacts.Jvm) {
if (CHECK_BYTECODE_LISTING !in module.directives) return
val dump = BytecodeListingTextCollectingVisitor.getText(
info.classFileFactory,
BytecodeListingTextCollectingVisitor.Filter.ForCodegenTests
)
multiModuleInfoDumper.builderForModule(module).append(dump)
}
override fun processAfterAllModules(someAssertionWasFailed: Boolean) {
if (multiModuleInfoDumper.isEmpty()) return
val suffix = if (testServices.defaultsProvider.defaultTargetBackend?.isIR == true) "_ir" else ""
val file = testServices.moduleStructure.originalTestDataFiles.first()
.withSuffixAndExtension(suffix, ".txt")
assertions.assertEqualsToFile(file, multiModuleInfoDumper.generateResultingDump())
}
}
@@ -0,0 +1,29 @@
/*
* 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.backend.handlers
import org.jetbrains.kotlin.codegen.D8Checker
import org.jetbrains.kotlin.codegen.DxChecker
import org.jetbrains.kotlin.test.directives.CodegenTestDirectives
import org.jetbrains.kotlin.test.directives.CodegenTestDirectives.IGNORE_DEXING
import org.jetbrains.kotlin.test.directives.CodegenTestDirectives.RUN_DEX_CHECKER
import org.jetbrains.kotlin.test.directives.model.DirectivesContainer
import org.jetbrains.kotlin.test.model.BinaryArtifacts
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.TestServices
class DxCheckerHandler(testServices: TestServices) : JvmBinaryArtifactHandler(testServices) {
override val directivesContainers: List<DirectivesContainer>
get() = listOf(CodegenTestDirectives)
override fun processModule(module: TestModule, info: BinaryArtifacts.Jvm) {
if (RUN_DEX_CHECKER !in module.directives || IGNORE_DEXING in module.directives) return
DxChecker.check(info.classFileFactory)
D8Checker.check(info.classFileFactory)
}
override fun processAfterAllModules(someAssertionWasFailed: Boolean) {}
}
@@ -9,20 +9,24 @@ import junit.framework.TestCase
import org.jetbrains.kotlin.backend.common.CodegenUtil.getMemberDeclarationsToGenerate
import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots
import org.jetbrains.kotlin.codegen.ClassFileFactory
import org.jetbrains.kotlin.codegen.CodegenTestUtil
import org.jetbrains.kotlin.codegen.GeneratedClassLoader
import org.jetbrains.kotlin.codegen.clearReflectionCache
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil.getFileClassInfoNoResolve
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.directives.CodegenTestDirectives
import org.jetbrains.kotlin.test.model.BinaryArtifacts
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.services.compilerConfigurationProvider
import org.jetbrains.kotlin.test.services.configuration.JvmEnvironmentConfigurator.Companion.TEST_CONFIGURATION_KIND_KEY
import org.jetbrains.kotlin.test.services.jvm.compiledClassesManager
import org.jetbrains.kotlin.utils.addToStdlib.runIf
import java.lang.reflect.Method
import java.net.URLClassLoader
class JvmBoxRunner(
testServices: TestServices
) : JvmBinaryArtifactHandler(testServices) {
class JvmBoxRunner(testServices: TestServices) : JvmBinaryArtifactHandler(testServices) {
companion object {
private val BOX_IN_SEPARATE_PROCESS_PORT = System.getProperty("kotlin.test.box.in.separate.process.port")
}
@@ -37,17 +41,45 @@ class JvmBoxRunner(
override fun processModule(module: TestModule, info: BinaryArtifacts.Jvm) {
val ktFiles = info.classFileFactory.inputFiles
val classLoader = createClassLoader(module, info.classFileFactory)
val reportProblems = module.targetBackend !in module.directives[CodegenTestDirectives.IGNORE_BACKEND]
val classLoader = createAndVerifyClassLoader(module, info.classFileFactory, reportProblems)
for (ktFile in ktFiles) {
val className = ktFile.getFacadeFqName() ?: continue
val clazz = classLoader.getGeneratedClass(className)
val method = clazz.getBoxMethodOrNull() ?: continue
boxMethodFound = true
callBoxMethodAndCheckResult(classLoader, clazz, method, unexpectedBehaviour = false)
callBoxMethodAndCheckResultWithCleanup(
info.classFileFactory,
classLoader,
clazz,
method,
unexpectedBehaviour = false,
reportProblems = reportProblems
)
return
}
}
private fun callBoxMethodAndCheckResultWithCleanup(
factory: ClassFileFactory,
classLoader: URLClassLoader,
clazz: Class<*>?,
method: Method,
unexpectedBehaviour: Boolean,
reportProblems: Boolean
) {
try {
callBoxMethodAndCheckResult(classLoader, clazz, method, unexpectedBehaviour)
} catch (e: Throwable) {
if (reportProblems) {
println(factory.createText())
}
throw e
} finally {
clearReflectionCache(classLoader)
}
}
private fun callBoxMethodAndCheckResult(
classLoader: URLClassLoader,
clazz: Class<*>?,
@@ -78,11 +110,34 @@ class JvmBoxRunner(
}
}
private fun createAndVerifyClassLoader(
module: TestModule,
classFileFactory: ClassFileFactory,
reportProblems: Boolean
): GeneratedClassLoader {
val classLoader = createClassLoader(module, classFileFactory)
val verificationSucceeded = CodegenTestUtil.verifyAllFilesWithAsm(classFileFactory, classLoader, reportProblems)
if (!verificationSucceeded ) {
assertions.fail { "Verification failed: see exceptions above" }
}
return classLoader
}
@OptIn(ExperimentalStdlibApi::class)
private fun createClassLoader(module: TestModule, classFileFactory: ClassFileFactory): GeneratedClassLoader {
val configuration = testServices.compilerConfigurationProvider.getCompilerConfiguration(module)
val urls = configuration.jvmClasspathRoots.map { it.toURI().toURL() }
val classLoader = URLClassLoader(urls.toTypedArray())
return GeneratedClassLoader(classFileFactory, classLoader)
val urls = buildList {
addAll(configuration.jvmClasspathRoots)
testServices.compiledClassesManager.getCompiledJavaDirForModule(module)?.let {
add(it)
}
}.map { it.toURI().toURL() }
val parentClassLoader = if (configuration[TEST_CONFIGURATION_KIND_KEY]?.withReflection == true) {
ForTestCompileRuntime.runtimeAndReflectJarClassLoader()
} else {
ForTestCompileRuntime.runtimeJarClassLoader()
}
return GeneratedClassLoader(classFileFactory, parentClassLoader, *urls.toTypedArray())
}
private fun KtFile.getFacadeFqName(): String? {
@@ -0,0 +1,20 @@
/*
* 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.backend.handlers
import org.jetbrains.kotlin.resolve.AnalyzingUtils
import org.jetbrains.kotlin.test.frontend.classic.ClassicFrontendOutputArtifact
import org.jetbrains.kotlin.test.frontend.classic.handlers.ClassicFrontendAnalysisHandler
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.TestServices
class NoCompilationErrorsHandler(testServices: TestServices) : ClassicFrontendAnalysisHandler(testServices) {
override fun processModule(module: TestModule, info: ClassicFrontendOutputArtifact) {
AnalyzingUtils.throwExceptionOnErrors(info.analysisResult.bindingContext)
}
override fun processAfterAllModules(someAssertionWasFailed: Boolean) {}
}
@@ -0,0 +1,25 @@
/*
* 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.backend.handlers
import org.jetbrains.kotlin.TestsCompiletimeError
import org.jetbrains.kotlin.resolve.AnalyzingUtils
import org.jetbrains.kotlin.test.model.BinaryArtifacts
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.TestServices
class NoJvmSpecificCompilationErrorsHandler(testServices: TestServices) : JvmBinaryArtifactHandler(testServices) {
override fun processModule(module: TestModule, info: BinaryArtifacts.Jvm) {
val generationState = info.classFileFactory.generationState
try {
AnalyzingUtils.throwExceptionOnErrors(generationState.collectedExtraJvmDiagnostics)
} catch (e: Throwable) {
throw TestsCompiletimeError(e)
}
}
override fun processAfterAllModules(someAssertionWasFailed: Boolean) {}
}
@@ -12,4 +12,27 @@ object CodegenTestDirectives : SimpleDirectivesContainer() {
val IGNORE_BACKEND by enumDirective<TargetBackend>(
description = "Ignore failures of test on target backend"
)
val JAVAC_OPTIONS by stringDirective(
description = "Specify javac options to compile java files"
)
val WITH_HELPERS by directive(
"""
Adds util functions for checking coroutines
See files in ./compiler/testData/codegen/helpers/
""".trimIndent()
)
val CHECK_BYTECODE_LISTING by directive(
description = "Dump resulting bytecode to .txt or _ir.txt file"
)
val RUN_DEX_CHECKER by directive(
description = "Run DxChecker and D8Checker"
)
val IGNORE_DEXING by directive(
description = "Ignore dex checkers"
)
}
@@ -24,8 +24,8 @@ import org.jetbrains.kotlin.test.frontend.classic.handlers.OldNewInferenceMetaIn
import org.jetbrains.kotlin.test.model.DependencyKind
import org.jetbrains.kotlin.test.model.FrontendKinds
import org.jetbrains.kotlin.test.services.configuration.CommonEnvironmentConfigurator
import org.jetbrains.kotlin.test.services.AdditionalDiagnosticsSourceFilesProvider
import org.jetbrains.kotlin.test.services.CoroutineHelpersSourceFilesProvider
import org.jetbrains.kotlin.test.services.sourceProviders.AdditionalDiagnosticsSourceFilesProvider
import org.jetbrains.kotlin.test.services.sourceProviders.CoroutineHelpersSourceFilesProvider
import org.jetbrains.kotlin.test.services.configuration.JvmEnvironmentConfigurator
import org.jetbrains.kotlin.test.services.configuration.ScriptingEnvironmentConfigurator
@@ -15,8 +15,8 @@ import org.jetbrains.kotlin.test.frontend.classic.handlers.OldNewInferenceMetaIn
import org.jetbrains.kotlin.test.model.DependencyKind
import org.jetbrains.kotlin.test.model.FrontendKinds
import org.jetbrains.kotlin.test.services.configuration.CommonEnvironmentConfigurator
import org.jetbrains.kotlin.test.services.AdditionalDiagnosticsSourceFilesProvider
import org.jetbrains.kotlin.test.services.CoroutineHelpersSourceFilesProvider
import org.jetbrains.kotlin.test.services.sourceProviders.AdditionalDiagnosticsSourceFilesProvider
import org.jetbrains.kotlin.test.services.sourceProviders.CoroutineHelpersSourceFilesProvider
abstract class AbstractDiagnosticsNativeTest : AbstractKotlinCompilerTest() {
override fun TestConfigurationBuilder.configuration() {
@@ -16,8 +16,8 @@ import org.jetbrains.kotlin.test.frontend.classic.handlers.OldNewInferenceMetaIn
import org.jetbrains.kotlin.test.model.DependencyKind
import org.jetbrains.kotlin.test.model.FrontendKinds
import org.jetbrains.kotlin.test.services.configuration.CommonEnvironmentConfigurator
import org.jetbrains.kotlin.test.services.AdditionalDiagnosticsSourceFilesProvider
import org.jetbrains.kotlin.test.services.CoroutineHelpersSourceFilesProvider
import org.jetbrains.kotlin.test.services.sourceProviders.AdditionalDiagnosticsSourceFilesProvider
import org.jetbrains.kotlin.test.services.sourceProviders.CoroutineHelpersSourceFilesProvider
import org.jetbrains.kotlin.test.services.configuration.JsEnvironmentConfigurator
abstract class AbstractDiagnosticsTestWithJsStdLib : AbstractKotlinCompilerTest() {
@@ -21,8 +21,8 @@ import org.jetbrains.kotlin.test.frontend.classic.handlers.OldNewInferenceMetaIn
import org.jetbrains.kotlin.test.model.DependencyKind
import org.jetbrains.kotlin.test.model.FrontendKinds
import org.jetbrains.kotlin.test.services.configuration.CommonEnvironmentConfigurator
import org.jetbrains.kotlin.test.services.AdditionalDiagnosticsSourceFilesProvider
import org.jetbrains.kotlin.test.services.CoroutineHelpersSourceFilesProvider
import org.jetbrains.kotlin.test.services.sourceProviders.AdditionalDiagnosticsSourceFilesProvider
import org.jetbrains.kotlin.test.services.sourceProviders.CoroutineHelpersSourceFilesProvider
import org.jetbrains.kotlin.test.services.configuration.JvmEnvironmentConfigurator
abstract class AbstractDiagnosticsTestWithJvmBackend : AbstractKotlinCompilerTest() {
@@ -21,8 +21,8 @@ import org.jetbrains.kotlin.test.frontend.fir.handlers.*
import org.jetbrains.kotlin.test.model.DependencyKind
import org.jetbrains.kotlin.test.model.FrontendKinds
import org.jetbrains.kotlin.test.services.configuration.CommonEnvironmentConfigurator
import org.jetbrains.kotlin.test.services.AdditionalDiagnosticsSourceFilesProvider
import org.jetbrains.kotlin.test.services.CoroutineHelpersSourceFilesProvider
import org.jetbrains.kotlin.test.services.sourceProviders.AdditionalDiagnosticsSourceFilesProvider
import org.jetbrains.kotlin.test.services.sourceProviders.CoroutineHelpersSourceFilesProvider
import org.jetbrains.kotlin.test.services.configuration.JvmEnvironmentConfigurator
import org.jetbrains.kotlin.test.services.fir.FirOldFrontendMetaConfigurator
@@ -20,10 +20,10 @@ import org.jetbrains.kotlin.test.frontend.classic.handlers.OldNewInferenceMetaIn
import org.jetbrains.kotlin.test.model.BackendKind
import org.jetbrains.kotlin.test.model.DependencyKind
import org.jetbrains.kotlin.test.model.FrontendKinds
import org.jetbrains.kotlin.test.services.AdditionalDiagnosticsSourceFilesProvider
import org.jetbrains.kotlin.test.services.CoroutineHelpersSourceFilesProvider
import org.jetbrains.kotlin.test.services.configuration.*
import org.jetbrains.kotlin.test.services.jvm.ForeignAnnotationAgainstCompiledJavaTestSuppressor
import org.jetbrains.kotlin.test.services.sourceProviders.AdditionalDiagnosticsSourceFilesProvider
import org.jetbrains.kotlin.test.services.sourceProviders.CoroutineHelpersSourceFilesProvider
abstract class AbstractForeignAnnotationsTestBase : AbstractKotlinCompilerTest() {
protected abstract val foreignAnnotationsConfigurator: Constructor<JvmForeignAnnotationsConfigurator>
@@ -76,7 +76,7 @@ abstract class AbstractKotlinCompilerTest {
builder.configuration()
}
fun runTest(@TestDataFile filePath: String) {
open fun runTest(@TestDataFile filePath: String) {
testRunner(filePath, configuration).runTest(filePath)
}
}
@@ -0,0 +1,22 @@
/*
* 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.runners
import org.jetbrains.kotlin.test.TargetBackend
import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
abstract class AbstractKotlinCompilerWithTargetBackendTest(
override val targetBackend: TargetBackend
) : AbstractKotlinCompilerTest(), RunnerWithTargetBackendForTestGeneratorMarker {
override fun configure(builder: TestConfigurationBuilder) {
super.configure(builder)
with(builder) {
globalDefaults {
targetBackend = this@AbstractKotlinCompilerWithTargetBackendTest.targetBackend
}
}
}
}
@@ -0,0 +1,22 @@
/*
* 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.runners.codegen
import org.jetbrains.kotlin.test.Constructor
import org.jetbrains.kotlin.test.TargetBackend
import org.jetbrains.kotlin.test.backend.classic.ClassicJvmBackendFacade
import org.jetbrains.kotlin.test.frontend.classic.ClassicFrontend2ClassicBackendConverter
import org.jetbrains.kotlin.test.model.BackendFacade
import org.jetbrains.kotlin.test.model.BinaryArtifacts
import org.jetbrains.kotlin.test.model.Frontend2BackendConverter
open class AbstractBlackBoxCodegenTest : AbstractJvmBlackBoxCodegenTestBase(TargetBackend.JVM) {
override val frontendToBackendConverter: Constructor<Frontend2BackendConverter<*, *>>
get() = ::ClassicFrontend2ClassicBackendConverter
override val backendFacade: Constructor<BackendFacade<*, BinaryArtifacts.Jvm>>
get() = ::ClassicJvmBackendFacade
}
@@ -0,0 +1,70 @@
/*
* 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.runners.codegen
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
import org.jetbrains.kotlin.test.Constructor
import org.jetbrains.kotlin.test.TargetBackend
import org.jetbrains.kotlin.test.backend.BlackBoxCodegenSuppressor
import org.jetbrains.kotlin.test.backend.handlers.*
import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
import org.jetbrains.kotlin.test.directives.CodegenTestDirectives.RUN_DEX_CHECKER
import org.jetbrains.kotlin.test.directives.DiagnosticsDirectives.REPORT_JVM_DIAGNOSTICS_ON_FRONTEND
import org.jetbrains.kotlin.test.directives.JvmEnvironmentConfigurationDirectives.USE_PSI_CLASS_FILES_READING
import org.jetbrains.kotlin.test.frontend.classic.ClassicFrontendFacade
import org.jetbrains.kotlin.test.model.*
import org.jetbrains.kotlin.test.runners.AbstractKotlinCompilerWithTargetBackendTest
import org.jetbrains.kotlin.test.services.configuration.CommonEnvironmentConfigurator
import org.jetbrains.kotlin.test.services.configuration.JvmEnvironmentConfigurator
import org.jetbrains.kotlin.test.services.sourceProviders.AdditionalDiagnosticsSourceFilesProvider
import org.jetbrains.kotlin.test.services.sourceProviders.CodegenHelpersSourceFilesProvider
import org.jetbrains.kotlin.test.services.sourceProviders.CoroutineHelpersSourceFilesProvider
abstract class AbstractJvmBlackBoxCodegenTestBase(
targetBackend: TargetBackend
) : AbstractKotlinCompilerWithTargetBackendTest(targetBackend) {
abstract val frontendToBackendConverter: Constructor<Frontend2BackendConverter<*, *>>
abstract val backendFacade: Constructor<BackendFacade<*, BinaryArtifacts.Jvm>>
override fun TestConfigurationBuilder.configuration() {
globalDefaults {
frontend = FrontendKinds.ClassicFrontend
targetPlatform = JvmPlatforms.defaultJvmPlatform
dependencyKind = DependencyKind.Binary
}
defaultDirectives {
+USE_PSI_CLASS_FILES_READING
+REPORT_JVM_DIAGNOSTICS_ON_FRONTEND
+RUN_DEX_CHECKER
}
useConfigurators(
::CommonEnvironmentConfigurator,
::JvmEnvironmentConfigurator
)
useAdditionalSourceProviders(
::AdditionalDiagnosticsSourceFilesProvider,
::CoroutineHelpersSourceFilesProvider,
::CodegenHelpersSourceFilesProvider,
)
useFrontendFacades(::ClassicFrontendFacade)
useFrontend2BackendConverters(frontendToBackendConverter)
useBackendFacades(backendFacade)
useFrontendHandlers(::NoCompilationErrorsHandler)
useArtifactsHandlers(
::JvmBoxRunner,
::NoJvmSpecificCompilationErrorsHandler,
::BytecodeListingHandler,
::DxCheckerHandler,
)
useAfterAnalysisCheckers(::BlackBoxCodegenSuppressor)
}
}
@@ -10,8 +10,10 @@ import com.intellij.openapi.util.SystemInfo
import org.jetbrains.kotlin.cli.jvm.config.addJavaSourceRoot
import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoot
import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots
import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.CompilerConfigurationKey
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
import org.jetbrains.kotlin.test.ConfigurationKind
@@ -24,18 +26,23 @@ import org.jetbrains.kotlin.test.model.DependencyDescription
import org.jetbrains.kotlin.test.model.DependencyKind
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.*
import org.jetbrains.kotlin.test.services.jvm.CompiledJarManager
import org.jetbrains.kotlin.test.services.jvm.compiledJarManager
import org.jetbrains.kotlin.test.services.jvm.CompiledClassesManager
import org.jetbrains.kotlin.test.services.jvm.compiledClassesManager
import org.jetbrains.kotlin.test.util.KtTestUtil
import org.jetbrains.kotlin.test.util.joinToArrayString
import org.jetbrains.kotlin.utils.addIfNotNull
import java.io.File
class JvmEnvironmentConfigurator(testServices: TestServices) : EnvironmentConfigurator(testServices) {
companion object {
val TEST_CONFIGURATION_KIND_KEY = CompilerConfigurationKey.create<ConfigurationKind>("ConfigurationKind")
}
override val directivesContainers: List<DirectivesContainer>
get() = listOf(JvmEnvironmentConfigurationDirectives)
override val additionalServices: List<ServiceRegistrationData>
get() = listOf(service(::CompiledJarManager))
get() = listOf(service(::CompiledClassesManager))
override fun configureCompilerConfiguration(configuration: CompilerConfiguration, module: TestModule, project: MockProject) {
if (module.targetPlatform !in JvmPlatforms.allJvmPlatforms) return
@@ -77,7 +84,9 @@ class JvmEnvironmentConfigurator(testServices: TestServices) : EnvironmentConfig
}
}
val configurationKind = extractConfigurationKind(registeredDirectives)
val configurationKind = extractConfigurationKind(registeredDirectives).also {
configuration.put(TEST_CONFIGURATION_KIND_KEY, it)
}
if (configurationKind.withRuntime) {
configuration.addJvmClasspathRoot(ForTestCompileRuntime.runtimeJarForTests())
@@ -119,6 +128,8 @@ class JvmEnvironmentConfigurator(testServices: TestServices) : EnvironmentConfig
if (JvmEnvironmentConfigurationDirectives.USE_PSI_CLASS_FILES_READING in module.directives) {
configuration.put(JVMConfigurationKeys.USE_PSI_CLASS_FILES_READING, true)
}
initBinaryDependencies(module, configuration)
}
private fun extractJdkKind(registeredDirectives: RegisteredDirectives): TestJdkKind {
@@ -146,12 +157,9 @@ class JvmEnvironmentConfigurator(testServices: TestServices) : EnvironmentConfig
if (noRuntime && withRuntime) {
error("NO_RUNTIME and WITH_RUNTIME can not be used together")
}
if (withReflect && !withRuntime) {
error("WITH_REFLECT may be used only with WITH_RUNTIME")
}
return when {
withRuntime && withReflect -> ConfigurationKind.ALL
withRuntime -> ConfigurationKind.NO_KOTLIN_REFLECT
withRuntime && !withReflect -> ConfigurationKind.NO_KOTLIN_REFLECT
withRuntime || withReflect -> ConfigurationKind.ALL
noRuntime -> ConfigurationKind.JDK_NO_RUNTIME
else -> ConfigurationKind.JDK_ONLY
}
@@ -164,9 +172,47 @@ class JvmEnvironmentConfigurator(testServices: TestServices) : EnvironmentConfig
.map { dependencyProvider.getTestModule(it.moduleName) }
.takeIf { it.isNotEmpty() }
?: return
val jarManager = testServices.compiledJarManager
val dependenciesClassPath = modulesFromDependencies.map { jarManager.getCompiledJarForModule(it) }
val jarManager = testServices.compiledClassesManager
val dependenciesClassPath = modulesFromDependencies.map { jarManager.getCompiledKotlinDirForModule(it) }
addJvmClasspathRoots(dependenciesClassPath)
}
}
@OptIn(ExperimentalStdlibApi::class)
private fun initBinaryDependencies(
module: TestModule,
configuration: CompilerConfiguration,
) {
val binaryDependencies = module.dependencies.filter { it.kind == DependencyKind.Binary }
val binaryFriends = module.friends.filter { it.kind == DependencyKind.Binary }
val dependencyProvider = testServices.dependencyProvider
val compiledClassesManager = testServices.compiledClassesManager
val compilerConfigurationProvider = testServices.compilerConfigurationProvider
fun addDependenciesToClasspath(dependencies: List<DependencyDescription>): List<File> {
val jvmClasspathRoots = buildList<File> {
dependencies.forEach {
val dependencyModule = dependencyProvider.getTestModule(it.moduleName)
add(compiledClassesManager.getCompiledKotlinDirForModule(dependencyModule))
addIfNotNull(compiledClassesManager.getCompiledJavaDirForModule(dependencyModule))
addAll(compilerConfigurationProvider.getCompilerConfiguration(dependencyModule).jvmClasspathRoots)
}
}
configuration.addJvmClasspathRoots(jvmClasspathRoots)
return jvmClasspathRoots
}
addDependenciesToClasspath(binaryDependencies)
addDependenciesToClasspath(binaryFriends)
if (binaryFriends.isNotEmpty()) {
configuration.put(JVMConfigurationKeys.FRIEND_PATHS, binaryFriends.flatMap {
val friendModule = dependencyProvider.getTestModule(it.moduleName)
listOfNotNull(
compiledClassesManager.getCompiledKotlinDirForModule(friendModule),
compiledClassesManager.getCompiledJavaDirForModule(friendModule)
)
}.map { it.absolutePath })
}
}
}
@@ -13,21 +13,8 @@ import org.jetbrains.kotlin.test.services.TestServices
class BackendKindExtractorImpl(testServices: TestServices) : BackendKindExtractor(testServices) {
override fun backendKind(targetBackend: TargetBackend?): BackendKind<*> {
return when (targetBackend) {
TargetBackend.ANY,
TargetBackend.JVM,
TargetBackend.JVM_OLD,
TargetBackend.ANDROID,
TargetBackend.JVM_MULTI_MODULE_OLD_AGAINST_IR -> BackendKinds.ClassicBackend
TargetBackend.JVM_IR,
TargetBackend.JVM_MULTI_MODULE_IR_AGAINST_OLD,
TargetBackend.JS,
TargetBackend.JS_IR,
TargetBackend.JS_IR_ES6,
TargetBackend.WASM -> BackendKinds.IrBackend
null -> BackendKind.NoBackend
}
if (targetBackend == null) return BackendKind.NoBackend
return if (targetBackend.isIR) BackendKinds.IrBackend
else BackendKinds.ClassicBackend
}
}
@@ -278,9 +278,14 @@ class ModuleStructureExtractorImpl(
}
private fun finishFile() {
val filename = currentFileName ?: defaultFileName
val actualDefaultFileName = if (currentModuleName == null) {
defaultFileName
} else {
"module_${currentModuleName}_$defaultFileName"
}
val filename = currentFileName ?: actualDefaultFileName
if (filesOfCurrentModule.any { it.name == filename }) {
error("File with name \"$filename\" already defined in module ${currentModuleName ?: defaultModuleName}")
error("File with name \"$filename\" already defined in module ${currentModuleName ?: actualDefaultFileName}")
}
filesOfCurrentModule.add(
TestFile(
@@ -0,0 +1,47 @@
/*
* 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.services.jvm
import org.jetbrains.kotlin.backend.common.output.SimpleOutputFileCollection
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.output.writeAll
import org.jetbrains.kotlin.codegen.ClassFileFactory
import org.jetbrains.kotlin.test.model.ArtifactKinds
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.*
import java.io.File
class CompiledClassesManager(val testServices: TestServices) : TestService {
private val compiledKotlinCache = mutableMapOf<TestModule, File>()
private val compiledJavaCache = mutableMapOf<TestModule, File>()
fun getCompiledKotlinDirForModule(module: TestModule, classFileFactory: ClassFileFactory? = null): File {
return compiledKotlinCache.getOrPut(module) {
val outputDir = testServices.createTempDirectory("module_${module.name}_kotlin-classes")
@Suppress("NAME_SHADOWING")
val classFileFactory = classFileFactory
?: testServices.dependencyProvider.getArtifact(module, ArtifactKinds.Jvm).classFileFactory
val outputFileCollection = SimpleOutputFileCollection(classFileFactory.currentOutput)
val messageCollector = testServices.compilerConfigurationProvider.getCompilerConfiguration(module)
.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
outputFileCollection.writeAll(outputDir, messageCollector, reportOutputFiles = false)
outputDir
}
}
fun getCompiledJavaDirForModule(module: TestModule): File? {
return compiledJavaCache[module]
}
fun getOrCreateCompiledJavaDirForModule(module: TestModule): File {
return compiledJavaCache.getOrPut(module) {
testServices.createTempDirectory("module_${module.name}_java-classes")
}
}
}
val TestServices.compiledClassesManager: CompiledClassesManager by TestServices.testServiceAccessor()
@@ -1,33 +0,0 @@
/*
* 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.services.jvm
import org.jetbrains.kotlin.backend.common.output.SimpleOutputFileCollection
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.output.writeAll
import org.jetbrains.kotlin.test.model.ArtifactKinds
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.*
import java.io.File
class CompiledJarManager(val testServices: TestServices) : TestService {
private val jarCache = mutableMapOf<TestModule, File>()
fun getCompiledJarForModule(module: TestModule): File {
return jarCache.getOrPut(module) {
val outputDir = testServices.createTempDirectory("module_${module.name}")
val classFileFactory = testServices.dependencyProvider.getArtifact(module, ArtifactKinds.Jvm).classFileFactory
val outputFileCollection = SimpleOutputFileCollection(classFileFactory.currentOutput)
val messageCollector = testServices.compilerConfigurationProvider.getCompilerConfiguration(module)
.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
outputFileCollection.writeAll(outputDir, messageCollector, reportOutputFiles = false)
classFileFactory.releaseGeneratedOutput()
outputDir
}
}
}
val TestServices.compiledJarManager: CompiledJarManager by TestServices.testServiceAccessor()
@@ -3,13 +3,15 @@
* 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.services
package org.jetbrains.kotlin.test.services.sourceProviders
import org.jetbrains.kotlin.test.directives.AdditionalFilesDirectives
import org.jetbrains.kotlin.test.directives.model.DirectivesContainer
import org.jetbrains.kotlin.test.directives.model.RegisteredDirectives
import org.jetbrains.kotlin.test.model.TestFile
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.AdditionalSourceProvider
import org.jetbrains.kotlin.test.services.TestServices
import java.io.File
class AdditionalDiagnosticsSourceFilesProvider(testServices: TestServices) : AdditionalSourceProvider(testServices) {
@@ -0,0 +1,40 @@
/*
* 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.services.sourceProviders
import org.jetbrains.kotlin.test.directives.CodegenTestDirectives
import org.jetbrains.kotlin.test.directives.model.DirectivesContainer
import org.jetbrains.kotlin.test.directives.model.RegisteredDirectives
import org.jetbrains.kotlin.test.model.TestFile
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.AdditionalSourceProvider
import org.jetbrains.kotlin.test.services.TestServices
import java.io.File
class CodegenHelpersSourceFilesProvider(testServices: TestServices) : AdditionalSourceProvider(testServices) {
companion object {
private const val HELPERS_PATH = "./compiler/testData/codegen/helpers"
private const val CLASSIC_BACKEND_PATH = "$HELPERS_PATH/CodegenTestHelpersOldBackend.kt"
private const val IR_BACKEND_PATH = "$HELPERS_PATH/CodegenTestHelpersIR.kt"
}
override val directives: List<DirectivesContainer> =
listOf(CodegenTestDirectives)
@OptIn(ExperimentalStdlibApi::class)
override fun produceAdditionalFiles(globalDirectives: RegisteredDirectives, module: TestModule): List<TestFile> {
if (CodegenTestDirectives.WITH_HELPERS !in module.directives) return emptyList()
return buildList {
val targetBackend = module.targetBackend ?: return@buildList
val helpersPath = if (targetBackend.isIR) {
IR_BACKEND_PATH
} else {
CLASSIC_BACKEND_PATH
}
add(File(helpersPath).toTestFile())
}
}
}
@@ -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.services
package org.jetbrains.kotlin.test.services.sourceProviders
import org.jetbrains.kotlin.test.directives.AdditionalFilesDirectives
import org.jetbrains.kotlin.test.directives.AdditionalFilesDirectives.CHECK_STATE_MACHINE
@@ -13,6 +13,8 @@ import org.jetbrains.kotlin.test.directives.model.DirectivesContainer
import org.jetbrains.kotlin.test.directives.model.RegisteredDirectives
import org.jetbrains.kotlin.test.model.TestFile
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.AdditionalSourceProvider
import org.jetbrains.kotlin.test.services.TestServices
import java.io.File
class CoroutineHelpersSourceFilesProvider(testServices: TestServices) : AdditionalSourceProvider(testServices) {
@@ -28,9 +28,13 @@ val File.firTestDataFile: File
}
fun File.withExtension(extension: String): File {
return withSuffixAndExtension(suffix = "", extension)
}
fun File.withSuffixAndExtension(suffix: String, extension: String): File {
@Suppress("NAME_SHADOWING")
val extension = extension.removePrefix(".")
return parentFile.resolve("$nameWithoutExtension.$extension")
return parentFile.resolve("$nameWithoutExtension$suffix.$extension")
}
/*