blob: 23caf3ff98a973b5c57e73fdf5be5cb43f39d6bc [file] [log] [blame]
/*
* 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.builders
import com.intellij.openapi.Disposable
import org.jetbrains.kotlin.fir.PrivateForInline
import org.jetbrains.kotlin.test.*
import org.jetbrains.kotlin.test.directives.model.DirectivesContainer
import org.jetbrains.kotlin.test.impl.TestConfigurationImpl
import org.jetbrains.kotlin.test.model.*
import org.jetbrains.kotlin.test.services.*
import kotlin.io.path.Path
@DefaultsDsl
@OptIn(TestInfrastructureInternals::class, PrivateForInline::class)
class TestConfigurationBuilder {
val defaultsProviderBuilder: DefaultsProviderBuilder = DefaultsProviderBuilder()
lateinit var assertions: AssertionsService
@PrivateForInline
val steps: MutableList<TestStepBuilder<*, *>> = mutableListOf()
@PrivateForInline
val namedSteps: MutableMap<String, TestStepBuilder<*, *>> = mutableMapOf()
private val sourcePreprocessors: MutableList<Constructor<SourceFilePreprocessor>> = mutableListOf()
private val additionalMetaInfoProcessors: MutableList<Constructor<AdditionalMetaInfoProcessor>> = mutableListOf()
private val environmentConfigurators: MutableList<Constructor<AbstractEnvironmentConfigurator>> = mutableListOf()
private val preAnalysisHandlers: MutableList<Constructor<PreAnalysisHandler>> = mutableListOf()
private val additionalSourceProviders: MutableList<Constructor<AdditionalSourceProvider>> = mutableListOf()
private val moduleStructureTransformers: MutableList<ModuleStructureTransformer> = mutableListOf()
private val metaTestConfigurators: MutableList<Constructor<MetaTestConfigurator>> = mutableListOf()
private val afterAnalysisCheckers: MutableList<Constructor<AfterAnalysisChecker>> = mutableListOf()
private var metaInfoHandlerEnabled: Boolean = false
private val directives: MutableList<DirectivesContainer> = mutableListOf()
val defaultRegisteredDirectivesBuilder: RegisteredDirectivesBuilder = RegisteredDirectivesBuilder()
private val configurationsByPositiveTestDataCondition: MutableList<Pair<Regex, TestConfigurationBuilder.() -> Unit>> = mutableListOf()
private val configurationsByNegativeTestDataCondition: MutableList<Pair<Regex, TestConfigurationBuilder.() -> Unit>> = mutableListOf()
private val additionalServices: MutableList<ServiceRegistrationData> = mutableListOf()
private var compilerConfigurationProvider: ((TestServices, Disposable, List<AbstractEnvironmentConfigurator>) -> CompilerConfigurationProvider)? = null
private var runtimeClasspathProviders: MutableList<Constructor<RuntimeClasspathProvider>> = mutableListOf()
lateinit var testInfo: KotlinTestInfo
lateinit var startingArtifactFactory: (TestModule) -> ResultingArtifact<*>
private val globalDefaultsConfigurators: MutableList<DefaultsProviderBuilder.() -> Unit> = mutableListOf()
private val defaultDirectiveConfigurators: MutableList<RegisteredDirectivesBuilder.() -> Unit> = mutableListOf()
inline fun <reified T : TestService> useAdditionalService(noinline serviceConstructor: (TestServices) -> T) {
useAdditionalServices(service(serviceConstructor))
}
fun useAdditionalServices(vararg serviceRegistrationData: ServiceRegistrationData) {
additionalServices += serviceRegistrationData
}
fun forTestsMatching(pattern: String, configuration: TestConfigurationBuilder.() -> Unit) {
val regex = pattern.toMatchingRegexString().toRegex()
forTestsMatching(regex, configuration)
}
fun forTestsNotMatching(pattern: String, configuration: TestConfigurationBuilder.() -> Unit) {
val regex = pattern.toMatchingRegexString().toRegex()
forTestsNotMatching(regex, configuration)
}
infix fun String.or(other: String): String {
return """$this|$other"""
}
private fun String.toMatchingRegexString(): String = when (this) {
"*" -> ".*"
else -> """^.*/(${replace("*", ".*")})$"""
}
fun forTestsMatching(pattern: Regex, configuration: TestConfigurationBuilder.() -> Unit) {
configurationsByPositiveTestDataCondition += pattern to configuration
}
fun forTestsNotMatching(pattern: Regex, configuration: TestConfigurationBuilder.() -> Unit) {
configurationsByNegativeTestDataCondition += pattern to configuration
}
fun globalDefaults(init: DefaultsProviderBuilder.() -> Unit) {
globalDefaultsConfigurators += init
defaultsProviderBuilder.apply(init)
}
fun <I : ResultingArtifact<I>, O : ResultingArtifact<O>> facadeStep(
facade: Constructor<AbstractTestFacade<I, O>>,
): FacadeStepBuilder<I, O> {
return FacadeStepBuilder(facade).also {
steps += it
}
}
inline fun <I : ResultingArtifact<I>> handlersStep(
artifactKind: TestArtifactKind<I>,
init: HandlersStepBuilder<I>.() -> Unit
): HandlersStepBuilder<I> {
return HandlersStepBuilder(artifactKind).also {
it.init()
steps += it
}
}
inline fun <I : ResultingArtifact<I>> namedHandlersStep(
name: String,
artifactKind: TestArtifactKind<I>,
init: HandlersStepBuilder<I>.() -> Unit
): HandlersStepBuilder<I> {
val previouslyContainedStep = namedStepOfType<I>(name)
if (previouslyContainedStep == null) {
val step = handlersStep(artifactKind, init)
namedSteps[name] = step
return step
} else {
configureNamedHandlersStep(name, artifactKind, init)
return previouslyContainedStep
}
}
inline fun <I : ResultingArtifact<I>> configureNamedHandlersStep(
name: String,
artifactKind: TestArtifactKind<I>,
init: HandlersStepBuilder<I>.() -> Unit
) {
val step = namedStepOfType<I>(name) ?: error { "Step \"$name\" not found" }
require(step.artifactKind == artifactKind) { "Step kind: ${step.artifactKind}, passed kind is $artifactKind" }
step.apply(init)
}
fun <I : ResultingArtifact<I>> namedStepOfType(name: String): HandlersStepBuilder<I>? {
@Suppress("UNCHECKED_CAST")
return namedSteps[name] as HandlersStepBuilder<I>?
}
fun useSourcePreprocessor(vararg preprocessors: Constructor<SourceFilePreprocessor>, needToPrepend: Boolean = false) {
if (needToPrepend) {
sourcePreprocessors.addAll(0, preprocessors.toList())
} else {
sourcePreprocessors.addAll(preprocessors)
}
}
fun useDirectives(vararg directives: DirectivesContainer) {
this.directives += directives
}
fun useConfigurators(vararg environmentConfigurators: Constructor<AbstractEnvironmentConfigurator>) {
this.environmentConfigurators += environmentConfigurators
}
fun usePreAnalysisHandlers(vararg handlers: Constructor<PreAnalysisHandler>) {
this.preAnalysisHandlers += handlers
}
fun useMetaInfoProcessors(vararg updaters: Constructor<AdditionalMetaInfoProcessor>) {
additionalMetaInfoProcessors += updaters
}
fun useAdditionalSourceProviders(vararg providers: Constructor<AdditionalSourceProvider>) {
additionalSourceProviders += providers
}
@TestInfrastructureInternals
fun resetModuleStructureTransformers() {
moduleStructureTransformers.clear()
}
@TestInfrastructureInternals
fun useModuleStructureTransformers(vararg transformers: ModuleStructureTransformer) {
moduleStructureTransformers += transformers
}
@TestInfrastructureInternals
fun useCustomCompilerConfigurationProvider(provider: (TestServices, Disposable, List<AbstractEnvironmentConfigurator>) -> CompilerConfigurationProvider) {
compilerConfigurationProvider = provider
}
fun useCustomRuntimeClasspathProviders(vararg provider: Constructor<RuntimeClasspathProvider>) {
runtimeClasspathProviders += provider
}
fun useMetaTestConfigurators(vararg configurators: Constructor<MetaTestConfigurator>) {
metaTestConfigurators += configurators
}
fun useAfterAnalysisCheckers(vararg checkers: Constructor<AfterAnalysisChecker>) {
afterAnalysisCheckers += checkers
}
fun defaultDirectives(init: RegisteredDirectivesBuilder.() -> Unit) {
defaultDirectiveConfigurators += init
defaultRegisteredDirectivesBuilder.apply(init)
}
fun enableMetaInfoHandler() {
metaInfoHandlerEnabled = true
}
fun build(testDataPath: String): TestConfiguration {
// We use URI here because we use '/' in our codebase, and URI also uses it (unlike OS-dependent `toString()`)
val absoluteTestDataPath = Path(testDataPath).normalize().toUri().toString()
for ((regex, configuration) in configurationsByPositiveTestDataCondition) {
if (regex.matches(absoluteTestDataPath)) {
this.configuration()
}
}
for ((regex, configuration) in configurationsByNegativeTestDataCondition) {
if (!regex.matches(absoluteTestDataPath)) {
this.configuration()
}
}
return TestConfigurationImpl(
testInfo,
defaultsProviderBuilder.build(),
assertions,
steps,
sourcePreprocessors,
additionalMetaInfoProcessors,
environmentConfigurators,
additionalSourceProviders,
preAnalysisHandlers,
moduleStructureTransformers,
metaTestConfigurators,
afterAnalysisCheckers,
compilerConfigurationProvider,
runtimeClasspathProviders,
metaInfoHandlerEnabled,
directives,
defaultRegisteredDirectivesBuilder.build(),
startingArtifactFactory,
additionalServices,
originalBuilder = ReadOnlyBuilder(this, testDataPath)
)
}
class ReadOnlyBuilder(private val builder: TestConfigurationBuilder, val testDataPath: String) {
val defaultsProviderBuilder: DefaultsProviderBuilder
get() = builder.defaultsProviderBuilder
val assertions: AssertionsService
get() = builder.assertions
val sourcePreprocessors: List<Constructor<SourceFilePreprocessor>>
get() = builder.sourcePreprocessors
val additionalMetaInfoProcessors: List<Constructor<AdditionalMetaInfoProcessor>>
get() = builder.additionalMetaInfoProcessors
val environmentConfigurators: List<Constructor<AbstractEnvironmentConfigurator>>
get() = builder.environmentConfigurators
val preAnalysisHandlers: List<Constructor<PreAnalysisHandler>>
get() = builder.preAnalysisHandlers
val additionalSourceProviders: List<Constructor<AdditionalSourceProvider>>
get() = builder.additionalSourceProviders
val moduleStructureTransformers: List<ModuleStructureTransformer>
get() = builder.moduleStructureTransformers
val metaTestConfigurators: List<Constructor<MetaTestConfigurator>>
get() = builder.metaTestConfigurators
val afterAnalysisCheckers: List<Constructor<AfterAnalysisChecker>>
get() = builder.afterAnalysisCheckers
val metaInfoHandlerEnabled: Boolean
get() = builder.metaInfoHandlerEnabled
val directives: List<DirectivesContainer>
get() = builder.directives
val defaultDirectiveConfigurators: List<RegisteredDirectivesBuilder.() -> Unit>
get() = builder.defaultDirectiveConfigurators
val globalDefaultsConfigurators: List<DefaultsProviderBuilder.() -> Unit>
get() = builder.globalDefaultsConfigurators
val configurationsByPositiveTestDataCondition: List<Pair<Regex, TestConfigurationBuilder.() -> Unit>>
get() = builder.configurationsByPositiveTestDataCondition
val configurationsByNegativeTestDataCondition: List<Pair<Regex, TestConfigurationBuilder.() -> Unit>>
get() = builder.configurationsByNegativeTestDataCondition
val additionalServices: List<ServiceRegistrationData>
get() = builder.additionalServices
val compilerConfigurationProvider: ((TestServices, Disposable, List<AbstractEnvironmentConfigurator>) -> CompilerConfigurationProvider)?
get() = builder.compilerConfigurationProvider
val runtimeClasspathProviders: List<Constructor<RuntimeClasspathProvider>>
get() = builder.runtimeClasspathProviders
val testInfo: KotlinTestInfo
get() = builder.testInfo
val startingArtifactFactory: (TestModule) -> ResultingArtifact<*>
get() = builder.startingArtifactFactory
}
}
inline fun testConfiguration(testDataPath: String, init: TestConfigurationBuilder.() -> Unit): TestConfiguration {
return TestConfigurationBuilder().apply(init).build(testDataPath)
}