[Test] Add ability to specify main class in Box tests
Also slightly refactor some existing services to make them more idiomatic
This commit is contained in:
committed by
TeamCityServer
parent
aab8870903
commit
a68c4ec7be
+6
-5
@@ -5,14 +5,15 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.test.services
|
package org.jetbrains.kotlin.test.services
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.test.model.TestModule
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
abstract class RuntimeClasspathProvider {
|
abstract class RuntimeClasspathProvider(val testServices: TestServices) {
|
||||||
abstract fun runtimeClassPaths(): List<File>
|
abstract fun runtimeClassPaths(module: TestModule): List<File>
|
||||||
}
|
}
|
||||||
|
|
||||||
class RuntimeClasspathProviderHolder(val providers: List<RuntimeClasspathProvider>) : TestService
|
class RuntimeClasspathProvidersContainer(val providers: List<RuntimeClasspathProvider>) : TestService
|
||||||
|
|
||||||
private val TestServices.runtimeClasspathProviderHolder: RuntimeClasspathProviderHolder by TestServices.testServiceAccessor()
|
private val TestServices.runtimeClasspathProviderContainer: RuntimeClasspathProvidersContainer by TestServices.testServiceAccessor()
|
||||||
val TestServices.runtimeClasspathProviders: List<RuntimeClasspathProvider>
|
val TestServices.runtimeClasspathProviders: List<RuntimeClasspathProvider>
|
||||||
get() = runtimeClasspathProviderHolder.providers
|
get() = runtimeClasspathProviderContainer.providers
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.test.services
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.fir.utils.ArrayMapAccessor
|
import org.jetbrains.kotlin.fir.utils.ArrayMapAccessor
|
||||||
import org.jetbrains.kotlin.fir.utils.ComponentArrayOwner
|
import org.jetbrains.kotlin.fir.utils.ComponentArrayOwner
|
||||||
|
import org.jetbrains.kotlin.fir.utils.NullableArrayMapAccessor
|
||||||
import org.jetbrains.kotlin.fir.utils.TypeRegistry
|
import org.jetbrains.kotlin.fir.utils.TypeRegistry
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
@@ -31,6 +32,10 @@ class TestServices : ComponentArrayOwner<TestService, TestService>(){
|
|||||||
inline fun <reified T : TestService> testServiceAccessor(): ArrayMapAccessor<TestService, TestService, T> {
|
inline fun <reified T : TestService> testServiceAccessor(): ArrayMapAccessor<TestService, TestService, T> {
|
||||||
return generateAccessor(T::class)
|
return generateAccessor(T::class)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline fun <reified T : TestService> nullableTestServiceAccessor(): NullableArrayMapAccessor<TestService, TestService, T> {
|
||||||
|
return generateNullableAccessor(T::class)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun register(data: ServiceRegistrationData) {
|
fun register(data: ServiceRegistrationData) {
|
||||||
|
|||||||
+24
-17
@@ -14,8 +14,8 @@ import org.jetbrains.kotlin.psi.KtFile
|
|||||||
import org.jetbrains.kotlin.test.TestJdkKind
|
import org.jetbrains.kotlin.test.TestJdkKind
|
||||||
import org.jetbrains.kotlin.test.backend.codegenSuppressionChecker
|
import org.jetbrains.kotlin.test.backend.codegenSuppressionChecker
|
||||||
import org.jetbrains.kotlin.test.clientserver.TestProxy
|
import org.jetbrains.kotlin.test.clientserver.TestProxy
|
||||||
import org.jetbrains.kotlin.test.directives.CodegenTestDirectives
|
|
||||||
import org.jetbrains.kotlin.test.directives.CodegenTestDirectives.ATTACH_DEBUGGER
|
import org.jetbrains.kotlin.test.directives.CodegenTestDirectives.ATTACH_DEBUGGER
|
||||||
|
import org.jetbrains.kotlin.test.directives.CodegenTestDirectives.REQUIRES_SEPARATE_PROCESS
|
||||||
import org.jetbrains.kotlin.test.directives.JvmEnvironmentConfigurationDirectives.JDK_KIND
|
import org.jetbrains.kotlin.test.directives.JvmEnvironmentConfigurationDirectives.JDK_KIND
|
||||||
import org.jetbrains.kotlin.test.directives.LanguageSettingsDirectives.ENABLE_JVM_PREVIEW
|
import org.jetbrains.kotlin.test.directives.LanguageSettingsDirectives.ENABLE_JVM_PREVIEW
|
||||||
import org.jetbrains.kotlin.test.directives.model.singleOrZeroValue
|
import org.jetbrains.kotlin.test.directives.model.singleOrZeroValue
|
||||||
@@ -23,9 +23,13 @@ import org.jetbrains.kotlin.test.model.BinaryArtifacts
|
|||||||
import org.jetbrains.kotlin.test.model.DependencyKind
|
import org.jetbrains.kotlin.test.model.DependencyKind
|
||||||
import org.jetbrains.kotlin.test.model.TestModule
|
import org.jetbrains.kotlin.test.model.TestModule
|
||||||
import org.jetbrains.kotlin.test.model.nameWithoutExtension
|
import org.jetbrains.kotlin.test.model.nameWithoutExtension
|
||||||
import org.jetbrains.kotlin.test.services.*
|
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.configuration.JvmEnvironmentConfigurator.Companion.TEST_CONFIGURATION_KIND_KEY
|
||||||
|
import org.jetbrains.kotlin.test.services.dependencyProvider
|
||||||
import org.jetbrains.kotlin.test.services.jvm.compiledClassesManager
|
import org.jetbrains.kotlin.test.services.jvm.compiledClassesManager
|
||||||
|
import org.jetbrains.kotlin.test.services.jvm.jvmBoxMainClassProvider
|
||||||
|
import org.jetbrains.kotlin.test.services.runtimeClasspathProviders
|
||||||
import org.jetbrains.kotlin.test.services.sourceProviders.MainFunctionForBlackBoxTestsSourceProvider
|
import org.jetbrains.kotlin.test.services.sourceProviders.MainFunctionForBlackBoxTestsSourceProvider
|
||||||
import org.jetbrains.kotlin.test.services.sourceProviders.MainFunctionForBlackBoxTestsSourceProvider.Companion.containsBoxMethod
|
import org.jetbrains.kotlin.test.services.sourceProviders.MainFunctionForBlackBoxTestsSourceProvider.Companion.containsBoxMethod
|
||||||
import org.jetbrains.kotlin.test.util.KtTestUtil
|
import org.jetbrains.kotlin.test.util.KtTestUtil
|
||||||
@@ -33,10 +37,8 @@ import org.jetbrains.kotlin.utils.addIfNotNull
|
|||||||
import org.jetbrains.kotlin.utils.addToStdlib.runIf
|
import org.jetbrains.kotlin.utils.addToStdlib.runIf
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.lang.reflect.Method
|
import java.lang.reflect.Method
|
||||||
import java.net.URI
|
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.net.URLClassLoader
|
import java.net.URLClassLoader
|
||||||
import java.nio.file.Paths
|
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
class JvmBoxRunner(testServices: TestServices) : JvmBinaryArtifactHandler(testServices) {
|
class JvmBoxRunner(testServices: TestServices) : JvmBinaryArtifactHandler(testServices) {
|
||||||
@@ -134,8 +136,8 @@ class JvmBoxRunner(testServices: TestServices) : JvmBinaryArtifactHandler(testSe
|
|||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
val jdkKind = module.directives.singleOrZeroValue(JDK_KIND)
|
val jdkKind = module.directives.singleOrZeroValue(JDK_KIND)
|
||||||
if (jdkKind?.requiresSeparateProcess == true) {
|
if (jdkKind?.requiresSeparateProcess == true || REQUIRES_SEPARATE_PROCESS in module.directives) {
|
||||||
runSeparateJvmInstance(module, jdkKind, classLoader, classFileFactory)
|
runSeparateJvmInstance(module, jdkKind ?: TestJdkKind.FULL_JDK, classLoader, classFileFactory)
|
||||||
} else {
|
} else {
|
||||||
runBoxInCurrentProcess(
|
runBoxInCurrentProcess(
|
||||||
classLoader,
|
classLoader,
|
||||||
@@ -181,6 +183,7 @@ class JvmBoxRunner(testServices: TestServices) : JvmBinaryArtifactHandler(testSe
|
|||||||
classFileFactory: ClassFileFactory
|
classFileFactory: ClassFileFactory
|
||||||
): String {
|
): String {
|
||||||
val jdkHome = when (jdkKind) {
|
val jdkHome = when (jdkKind) {
|
||||||
|
TestJdkKind.FULL_JDK -> KtTestUtil.getJdk8Home()
|
||||||
TestJdkKind.FULL_JDK_11 -> KtTestUtil.getJdk11Home()
|
TestJdkKind.FULL_JDK_11 -> KtTestUtil.getJdk11Home()
|
||||||
TestJdkKind.FULL_JDK_17 -> KtTestUtil.getJdk17Home()
|
TestJdkKind.FULL_JDK_17 -> KtTestUtil.getJdk17Home()
|
||||||
else -> error("Unsupported JDK kind: $jdkKind")
|
else -> error("Unsupported JDK kind: $jdkKind")
|
||||||
@@ -192,14 +195,18 @@ class JvmBoxRunner(testServices: TestServices) : JvmBinaryArtifactHandler(testSe
|
|||||||
|
|
||||||
val classPath = extractClassPath(module, classLoader, classFileFactory)
|
val classPath = extractClassPath(module, classLoader, classFileFactory)
|
||||||
|
|
||||||
val mainFile = module.files.firstOrNull {
|
val mainClassAndArguments = testServices.jvmBoxMainClassProvider?.getMainClassNameAndAdditionalArguments() ?: run {
|
||||||
it.name == MainFunctionForBlackBoxTestsSourceProvider.BOX_MAIN_FILE_NAME && it.isAdditional
|
val mainFile = module.files.firstOrNull {
|
||||||
} ?: error("No file with main function was generated. Please check TODO source provider")
|
it.name == MainFunctionForBlackBoxTestsSourceProvider.BOX_MAIN_FILE_NAME && it.isAdditional
|
||||||
|
} ?: error("No file with main function was generated. Please check TODO source provider")
|
||||||
|
|
||||||
val mainFqName = listOfNotNull(
|
val mainFqName = listOfNotNull(
|
||||||
MainFunctionForBlackBoxTestsSourceProvider.detectPackage(mainFile),
|
MainFunctionForBlackBoxTestsSourceProvider.detectPackage(mainFile),
|
||||||
"${mainFile.nameWithoutExtension}Kt"
|
"${mainFile.nameWithoutExtension}Kt"
|
||||||
).joinToString(".")
|
).joinToString(".")
|
||||||
|
|
||||||
|
listOf(mainFqName)
|
||||||
|
}
|
||||||
|
|
||||||
val command = listOfNotNull(
|
val command = listOfNotNull(
|
||||||
javaExe.absolutePath,
|
javaExe.absolutePath,
|
||||||
@@ -208,8 +215,8 @@ class JvmBoxRunner(testServices: TestServices) : JvmBinaryArtifactHandler(testSe
|
|||||||
runIf(ENABLE_JVM_PREVIEW in module.directives) { "--enable-preview" },
|
runIf(ENABLE_JVM_PREVIEW in module.directives) { "--enable-preview" },
|
||||||
"-classpath",
|
"-classpath",
|
||||||
classPath.joinToString(File.pathSeparator, transform = { File(it.toURI()).absolutePath }),
|
classPath.joinToString(File.pathSeparator, transform = { File(it.toURI()).absolutePath }),
|
||||||
mainFqName,
|
) + mainClassAndArguments
|
||||||
)
|
|
||||||
val process = ProcessBuilder(command).start()
|
val process = ProcessBuilder(command).start()
|
||||||
process.waitFor(1, TimeUnit.MINUTES)
|
process.waitFor(1, TimeUnit.MINUTES)
|
||||||
return try {
|
return try {
|
||||||
@@ -268,7 +275,7 @@ class JvmBoxRunner(testServices: TestServices) : JvmBinaryArtifactHandler(testSe
|
|||||||
reportProblems: Boolean
|
reportProblems: Boolean
|
||||||
): GeneratedClassLoader {
|
): GeneratedClassLoader {
|
||||||
val classLoader = createClassLoader(module, classFileFactory)
|
val classLoader = createClassLoader(module, classFileFactory)
|
||||||
if (module.directives.singleOrZeroValue(JDK_KIND)?.requiresSeparateProcess != true) {
|
if (REQUIRES_SEPARATE_PROCESS !in module.directives && module.directives.singleOrZeroValue(JDK_KIND)?.requiresSeparateProcess != true) {
|
||||||
val verificationSucceeded = CodegenTestUtil.verifyAllFilesWithAsm(classFileFactory, classLoader, reportProblems)
|
val verificationSucceeded = CodegenTestUtil.verifyAllFilesWithAsm(classFileFactory, classLoader, reportProblems)
|
||||||
if (!verificationSucceeded) {
|
if (!verificationSucceeded) {
|
||||||
assertions.fail { "Verification failed: see exceptions above" }
|
assertions.fail { "Verification failed: see exceptions above" }
|
||||||
@@ -308,7 +315,7 @@ class JvmBoxRunner(testServices: TestServices) : JvmBinaryArtifactHandler(testSe
|
|||||||
}
|
}
|
||||||
|
|
||||||
computeClasspath(rootModule, true)
|
computeClasspath(rootModule, true)
|
||||||
testServices.runtimeClasspathProviders.flatMapTo(result) { it.runtimeClassPaths() }
|
testServices.runtimeClasspathProviders.flatMapTo(result) { it.runtimeClassPaths(rootModule) }
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -184,7 +184,7 @@ class TestConfigurationBuilder {
|
|||||||
compilerConfigurationProvider = provider
|
compilerConfigurationProvider = provider
|
||||||
}
|
}
|
||||||
|
|
||||||
fun useCustomRuntimeClasspathProvider(provider: Constructor<RuntimeClasspathProvider>) {
|
fun useCustomRuntimeClasspathProviders(vararg provider: Constructor<RuntimeClasspathProvider>) {
|
||||||
runtimeClasspathProviders += provider
|
runtimeClasspathProviders += provider
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+7
@@ -164,4 +164,11 @@ object CodegenTestDirectives : SimpleDirectivesContainer() {
|
|||||||
Please don't forget to remove this directive after debug session is over
|
Please don't forget to remove this directive after debug session is over
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val REQUIRES_SEPARATE_PROCESS by directive(
|
||||||
|
description = """
|
||||||
|
Force run `box` method in separate process even if jdk of current process
|
||||||
|
is same as required jdk for test
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -53,7 +53,7 @@ class TestConfigurationImpl(
|
|||||||
init {
|
init {
|
||||||
testServices.register(KotlinTestInfo::class, testInfo)
|
testServices.register(KotlinTestInfo::class, testInfo)
|
||||||
val runtimeClassPathProviders = runtimeClasspathProviders.map { it.invoke(testServices) }
|
val runtimeClassPathProviders = runtimeClasspathProviders.map { it.invoke(testServices) }
|
||||||
testServices.register(RuntimeClasspathProviderHolder::class, RuntimeClasspathProviderHolder(runtimeClassPathProviders))
|
testServices.register(RuntimeClasspathProvidersContainer::class, RuntimeClasspathProvidersContainer(runtimeClassPathProviders))
|
||||||
additionalServices.forEach { testServices.register(it) }
|
additionalServices.forEach { testServices.register(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* 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.services.jvm
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.test.services.TestService
|
||||||
|
import org.jetbrains.kotlin.test.services.TestServices
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This service is used to provide main class and arguments for invoke of `box` method
|
||||||
|
* in separate JVM instance if it somehow differs from default implementation, which is
|
||||||
|
*
|
||||||
|
* fun main() {
|
||||||
|
* val result = box()
|
||||||
|
* if (result != "OK") {
|
||||||
|
* throw AssertionError()
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* Please note that can work only if running in separate JDK is enabled,
|
||||||
|
* see [CodegenTestDirectives.REQUIRES_SEPARATE_PROCESS] directive
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
abstract class JvmBoxMainClassProvider(val testServices: TestServices) : TestService {
|
||||||
|
abstract fun getMainClassNameAndAdditionalArguments(): List<String>
|
||||||
|
}
|
||||||
|
|
||||||
|
val TestServices.jvmBoxMainClassProvider: JvmBoxMainClassProvider? by TestServices.nullableTestServiceAccessor()
|
||||||
+4
-2
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.test.services.sourceProviders
|
package org.jetbrains.kotlin.test.services.sourceProviders
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.test.directives.CodegenTestDirectives.REQUIRES_SEPARATE_PROCESS
|
||||||
import org.jetbrains.kotlin.test.directives.JvmEnvironmentConfigurationDirectives.JDK_KIND
|
import org.jetbrains.kotlin.test.directives.JvmEnvironmentConfigurationDirectives.JDK_KIND
|
||||||
import org.jetbrains.kotlin.test.directives.model.RegisteredDirectives
|
import org.jetbrains.kotlin.test.directives.model.RegisteredDirectives
|
||||||
import org.jetbrains.kotlin.test.directives.model.singleOrZeroValue
|
import org.jetbrains.kotlin.test.directives.model.singleOrZeroValue
|
||||||
@@ -36,8 +37,9 @@ class MainFunctionForBlackBoxTestsSourceProvider(testServices: TestServices) : A
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun produceAdditionalFiles(globalDirectives: RegisteredDirectives, module: TestModule): List<TestFile> {
|
override fun produceAdditionalFiles(globalDirectives: RegisteredDirectives, module: TestModule): List<TestFile> {
|
||||||
val jdkKind = module.directives.singleOrZeroValue(JDK_KIND) ?: return emptyList()
|
if (REQUIRES_SEPARATE_PROCESS !in module.directives && module.directives.singleOrZeroValue(JDK_KIND)?.requiresSeparateProcess != true) {
|
||||||
if (!jdkKind.requiresSeparateProcess) return emptyList()
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
val fileWithBox = module.files.firstOrNull { containsBoxMethod(it) } ?: return emptyList()
|
val fileWithBox = module.files.firstOrNull { containsBoxMethod(it) } ?: return emptyList()
|
||||||
|
|
||||||
|
|||||||
+7
-4
@@ -63,9 +63,12 @@ internal fun TestConfigurationBuilder.configureForKotlinxSerialization(libraries
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
useCustomRuntimeClasspathProvider {
|
|
||||||
object : RuntimeClasspathProvider() {
|
useCustomRuntimeClasspathProviders(
|
||||||
override fun runtimeClassPaths(): List<File> = librariesPaths
|
{
|
||||||
|
object : RuntimeClasspathProvider(it) {
|
||||||
|
override fun runtimeClassPaths(module: TestModule): List<File> = librariesPaths
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user