[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
|
||||
|
||||
import org.jetbrains.kotlin.test.model.TestModule
|
||||
import java.io.File
|
||||
|
||||
abstract class RuntimeClasspathProvider {
|
||||
abstract fun runtimeClassPaths(): List<File>
|
||||
abstract class RuntimeClasspathProvider(val testServices: TestServices) {
|
||||
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>
|
||||
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.ComponentArrayOwner
|
||||
import org.jetbrains.kotlin.fir.utils.NullableArrayMapAccessor
|
||||
import org.jetbrains.kotlin.fir.utils.TypeRegistry
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
@@ -31,6 +32,10 @@ class TestServices : ComponentArrayOwner<TestService, TestService>(){
|
||||
inline fun <reified T : TestService> testServiceAccessor(): ArrayMapAccessor<TestService, TestService, T> {
|
||||
return generateAccessor(T::class)
|
||||
}
|
||||
|
||||
inline fun <reified T : TestService> nullableTestServiceAccessor(): NullableArrayMapAccessor<TestService, TestService, T> {
|
||||
return generateNullableAccessor(T::class)
|
||||
}
|
||||
}
|
||||
|
||||
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.backend.codegenSuppressionChecker
|
||||
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.REQUIRES_SEPARATE_PROCESS
|
||||
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.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.TestModule
|
||||
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.dependencyProvider
|
||||
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.Companion.containsBoxMethod
|
||||
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 java.io.File
|
||||
import java.lang.reflect.Method
|
||||
import java.net.URI
|
||||
import java.net.URL
|
||||
import java.net.URLClassLoader
|
||||
import java.nio.file.Paths
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class JvmBoxRunner(testServices: TestServices) : JvmBinaryArtifactHandler(testServices) {
|
||||
@@ -134,8 +136,8 @@ class JvmBoxRunner(testServices: TestServices) : JvmBinaryArtifactHandler(testSe
|
||||
)
|
||||
} else {
|
||||
val jdkKind = module.directives.singleOrZeroValue(JDK_KIND)
|
||||
if (jdkKind?.requiresSeparateProcess == true) {
|
||||
runSeparateJvmInstance(module, jdkKind, classLoader, classFileFactory)
|
||||
if (jdkKind?.requiresSeparateProcess == true || REQUIRES_SEPARATE_PROCESS in module.directives) {
|
||||
runSeparateJvmInstance(module, jdkKind ?: TestJdkKind.FULL_JDK, classLoader, classFileFactory)
|
||||
} else {
|
||||
runBoxInCurrentProcess(
|
||||
classLoader,
|
||||
@@ -181,6 +183,7 @@ class JvmBoxRunner(testServices: TestServices) : JvmBinaryArtifactHandler(testSe
|
||||
classFileFactory: ClassFileFactory
|
||||
): String {
|
||||
val jdkHome = when (jdkKind) {
|
||||
TestJdkKind.FULL_JDK -> KtTestUtil.getJdk8Home()
|
||||
TestJdkKind.FULL_JDK_11 -> KtTestUtil.getJdk11Home()
|
||||
TestJdkKind.FULL_JDK_17 -> KtTestUtil.getJdk17Home()
|
||||
else -> error("Unsupported JDK kind: $jdkKind")
|
||||
@@ -192,14 +195,18 @@ class JvmBoxRunner(testServices: TestServices) : JvmBinaryArtifactHandler(testSe
|
||||
|
||||
val classPath = extractClassPath(module, classLoader, classFileFactory)
|
||||
|
||||
val mainFile = module.files.firstOrNull {
|
||||
it.name == MainFunctionForBlackBoxTestsSourceProvider.BOX_MAIN_FILE_NAME && it.isAdditional
|
||||
} ?: error("No file with main function was generated. Please check TODO source provider")
|
||||
val mainClassAndArguments = testServices.jvmBoxMainClassProvider?.getMainClassNameAndAdditionalArguments() ?: run {
|
||||
val mainFile = module.files.firstOrNull {
|
||||
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(
|
||||
MainFunctionForBlackBoxTestsSourceProvider.detectPackage(mainFile),
|
||||
"${mainFile.nameWithoutExtension}Kt"
|
||||
).joinToString(".")
|
||||
val mainFqName = listOfNotNull(
|
||||
MainFunctionForBlackBoxTestsSourceProvider.detectPackage(mainFile),
|
||||
"${mainFile.nameWithoutExtension}Kt"
|
||||
).joinToString(".")
|
||||
|
||||
listOf(mainFqName)
|
||||
}
|
||||
|
||||
val command = listOfNotNull(
|
||||
javaExe.absolutePath,
|
||||
@@ -208,8 +215,8 @@ class JvmBoxRunner(testServices: TestServices) : JvmBinaryArtifactHandler(testSe
|
||||
runIf(ENABLE_JVM_PREVIEW in module.directives) { "--enable-preview" },
|
||||
"-classpath",
|
||||
classPath.joinToString(File.pathSeparator, transform = { File(it.toURI()).absolutePath }),
|
||||
mainFqName,
|
||||
)
|
||||
) + mainClassAndArguments
|
||||
|
||||
val process = ProcessBuilder(command).start()
|
||||
process.waitFor(1, TimeUnit.MINUTES)
|
||||
return try {
|
||||
@@ -268,7 +275,7 @@ class JvmBoxRunner(testServices: TestServices) : JvmBinaryArtifactHandler(testSe
|
||||
reportProblems: Boolean
|
||||
): GeneratedClassLoader {
|
||||
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)
|
||||
if (!verificationSucceeded) {
|
||||
assertions.fail { "Verification failed: see exceptions above" }
|
||||
@@ -308,7 +315,7 @@ class JvmBoxRunner(testServices: TestServices) : JvmBinaryArtifactHandler(testSe
|
||||
}
|
||||
|
||||
computeClasspath(rootModule, true)
|
||||
testServices.runtimeClasspathProviders.flatMapTo(result) { it.runtimeClassPaths() }
|
||||
testServices.runtimeClasspathProviders.flatMapTo(result) { it.runtimeClassPaths(rootModule) }
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -184,7 +184,7 @@ class TestConfigurationBuilder {
|
||||
compilerConfigurationProvider = provider
|
||||
}
|
||||
|
||||
fun useCustomRuntimeClasspathProvider(provider: Constructor<RuntimeClasspathProvider>) {
|
||||
fun useCustomRuntimeClasspathProviders(vararg provider: Constructor<RuntimeClasspathProvider>) {
|
||||
runtimeClasspathProviders += provider
|
||||
}
|
||||
|
||||
|
||||
+7
@@ -164,4 +164,11 @@ object CodegenTestDirectives : SimpleDirectivesContainer() {
|
||||
Please don't forget to remove this directive after debug session is over
|
||||
""".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 {
|
||||
testServices.register(KotlinTestInfo::class, testInfo)
|
||||
val runtimeClassPathProviders = runtimeClasspathProviders.map { it.invoke(testServices) }
|
||||
testServices.register(RuntimeClasspathProviderHolder::class, RuntimeClasspathProviderHolder(runtimeClassPathProviders))
|
||||
testServices.register(RuntimeClasspathProvidersContainer::class, RuntimeClasspathProvidersContainer(runtimeClassPathProviders))
|
||||
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
|
||||
|
||||
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.model.RegisteredDirectives
|
||||
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> {
|
||||
val jdkKind = module.directives.singleOrZeroValue(JDK_KIND) ?: return emptyList()
|
||||
if (!jdkKind.requiresSeparateProcess) return emptyList()
|
||||
if (REQUIRES_SEPARATE_PROCESS !in module.directives && module.directives.singleOrZeroValue(JDK_KIND)?.requiresSeparateProcess != true) {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
val fileWithBox = module.files.firstOrNull { containsBoxMethod(it) } ?: return emptyList()
|
||||
|
||||
|
||||
+8
-5
@@ -63,9 +63,12 @@ internal fun TestConfigurationBuilder.configureForKotlinxSerialization(libraries
|
||||
}
|
||||
}
|
||||
})
|
||||
useCustomRuntimeClasspathProvider {
|
||||
object : RuntimeClasspathProvider() {
|
||||
override fun runtimeClassPaths(): List<File> = librariesPaths
|
||||
|
||||
useCustomRuntimeClasspathProviders(
|
||||
{
|
||||
object : RuntimeClasspathProvider(it) {
|
||||
override fun runtimeClassPaths(module: TestModule): List<File> = librariesPaths
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user