From a68c4ec7be1e03bbd14868bdd3238d7906537db2 Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Wed, 6 Oct 2021 15:03:48 +0300 Subject: [PATCH] [Test] Add ability to specify main class in Box tests Also slightly refactor some existing services to make them more idiomatic --- .../test/services/RuntimeClasspathProvider.kt | 11 ++--- .../kotlin/test/services/TestServices.kt | 5 +++ .../test/backend/handlers/JvmBoxRunner.kt | 41 +++++++++++-------- .../test/builders/TestConfigurationBuilder.kt | 2 +- .../test/directives/CodegenTestDirectives.kt | 7 ++++ .../kotlin/test/impl/TestConfigurationImpl.kt | 2 +- .../services/jvm/JvmBoxMainClassProvider.kt | 30 ++++++++++++++ ...nFunctionForBlackBoxTestsSourceProvider.kt | 6 ++- .../kotlinx/serialization/RuntimeSearch.kt | 13 +++--- 9 files changed, 86 insertions(+), 31 deletions(-) create mode 100644 compiler/tests-common-new/tests/org/jetbrains/kotlin/test/services/jvm/JvmBoxMainClassProvider.kt diff --git a/compiler/test-infrastructure/tests/org/jetbrains/kotlin/test/services/RuntimeClasspathProvider.kt b/compiler/test-infrastructure/tests/org/jetbrains/kotlin/test/services/RuntimeClasspathProvider.kt index f477bdf0f0c..5729831889d 100644 --- a/compiler/test-infrastructure/tests/org/jetbrains/kotlin/test/services/RuntimeClasspathProvider.kt +++ b/compiler/test-infrastructure/tests/org/jetbrains/kotlin/test/services/RuntimeClasspathProvider.kt @@ -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 +abstract class RuntimeClasspathProvider(val testServices: TestServices) { + abstract fun runtimeClassPaths(module: TestModule): List } -class RuntimeClasspathProviderHolder(val providers: List) : TestService +class RuntimeClasspathProvidersContainer(val providers: List) : TestService -private val TestServices.runtimeClasspathProviderHolder: RuntimeClasspathProviderHolder by TestServices.testServiceAccessor() +private val TestServices.runtimeClasspathProviderContainer: RuntimeClasspathProvidersContainer by TestServices.testServiceAccessor() val TestServices.runtimeClasspathProviders: List - get() = runtimeClasspathProviderHolder.providers + get() = runtimeClasspathProviderContainer.providers diff --git a/compiler/test-infrastructure/tests/org/jetbrains/kotlin/test/services/TestServices.kt b/compiler/test-infrastructure/tests/org/jetbrains/kotlin/test/services/TestServices.kt index 1febc71e29e..14925deae62 100644 --- a/compiler/test-infrastructure/tests/org/jetbrains/kotlin/test/services/TestServices.kt +++ b/compiler/test-infrastructure/tests/org/jetbrains/kotlin/test/services/TestServices.kt @@ -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(){ inline fun testServiceAccessor(): ArrayMapAccessor { return generateAccessor(T::class) } + + inline fun nullableTestServiceAccessor(): NullableArrayMapAccessor { + return generateNullableAccessor(T::class) + } } fun register(data: ServiceRegistrationData) { diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/backend/handlers/JvmBoxRunner.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/backend/handlers/JvmBoxRunner.kt index 9de0845975e..ca7918b726f 100644 --- a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/backend/handlers/JvmBoxRunner.kt +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/backend/handlers/JvmBoxRunner.kt @@ -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 } diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/builders/TestConfigurationBuilder.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/builders/TestConfigurationBuilder.kt index b98b5f9791c..9a1de8bf80f 100644 --- a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/builders/TestConfigurationBuilder.kt +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/builders/TestConfigurationBuilder.kt @@ -184,7 +184,7 @@ class TestConfigurationBuilder { compilerConfigurationProvider = provider } - fun useCustomRuntimeClasspathProvider(provider: Constructor) { + fun useCustomRuntimeClasspathProviders(vararg provider: Constructor) { runtimeClasspathProviders += provider } diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/directives/CodegenTestDirectives.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/directives/CodegenTestDirectives.kt index 05817a5ebd9..d0c56450876 100644 --- a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/directives/CodegenTestDirectives.kt +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/directives/CodegenTestDirectives.kt @@ -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() + ) } diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/impl/TestConfigurationImpl.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/impl/TestConfigurationImpl.kt index 8ec4e0f274e..a4636d5471a 100644 --- a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/impl/TestConfigurationImpl.kt +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/impl/TestConfigurationImpl.kt @@ -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) } } diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/services/jvm/JvmBoxMainClassProvider.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/services/jvm/JvmBoxMainClassProvider.kt new file mode 100644 index 00000000000..c3fe6c9f839 --- /dev/null +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/services/jvm/JvmBoxMainClassProvider.kt @@ -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 +} + +val TestServices.jvmBoxMainClassProvider: JvmBoxMainClassProvider? by TestServices.nullableTestServiceAccessor() diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/services/sourceProviders/MainFunctionForBlackBoxTestsSourceProvider.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/services/sourceProviders/MainFunctionForBlackBoxTestsSourceProvider.kt index 54d935212de..f5ac4fcfd69 100644 --- a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/services/sourceProviders/MainFunctionForBlackBoxTestsSourceProvider.kt +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/services/sourceProviders/MainFunctionForBlackBoxTestsSourceProvider.kt @@ -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 { - 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() diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/RuntimeSearch.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/RuntimeSearch.kt index 1b99d2a906b..20108ef6dea 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/RuntimeSearch.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/test/org/jetbrains/kotlinx/serialization/RuntimeSearch.kt @@ -63,9 +63,12 @@ internal fun TestConfigurationBuilder.configureForKotlinxSerialization(libraries } } }) - useCustomRuntimeClasspathProvider { - object : RuntimeClasspathProvider() { - override fun runtimeClassPaths(): List = librariesPaths + + useCustomRuntimeClasspathProviders( + { + object : RuntimeClasspathProvider(it) { + override fun runtimeClassPaths(module: TestModule): List = librariesPaths + } } - } -} \ No newline at end of file + ) +}