[Commonizer] Clean-up in AbstractCommonizationFromSourcesTest

This commit is contained in:
Dmitriy Dolovov
2020-06-03 14:14:15 +07:00
parent 5076f981a6
commit c8ba3fa8ed
@@ -5,11 +5,13 @@
package org.jetbrains.kotlin.descriptors.commonizer
import com.intellij.openapi.Disposable
import com.intellij.testFramework.PlatformTestUtil.lowercaseFirstLetter
import org.jetbrains.kotlin.analyzer.common.CommonResolverForModuleFactory
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.languageVersionSettings
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.commonizer.utils.assertCommonizationPerformed
@@ -18,14 +20,15 @@ import org.jetbrains.kotlin.descriptors.commonizer.utils.assertModulesAreEqual
import org.jetbrains.kotlin.descriptors.commonizer.utils.assertValidModule
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.platform.CommonPlatforms
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.test.KotlinTestUtils.*
import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase
import java.io.File
import kotlin.contracts.ExperimentalContracts
import kotlin.test.fail
@ExperimentalContracts
@Suppress("MemberVisibilityCanBePrivate")
abstract class AbstractCommonizationFromSourcesTest : KtUsefulTestCase() {
companion object {
init {
@@ -35,137 +38,45 @@ abstract class AbstractCommonizationFromSourcesTest : KtUsefulTestCase() {
fun Collection<ModuleDescriptor>.eachModuleAsTarget() = mapIndexed { index, moduleDescriptor ->
InputTarget("target_$index") to moduleDescriptor
}.toMap().toCommonizationParameters()
fun Map<InputTarget, ModuleDescriptor>.toCommonizationParameters() = Parameters().also {
forEach { (target, moduleDescriptor) ->
it.addTarget(
TargetProvider(
target = target,
builtInsClass = moduleDescriptor.builtIns::class.java,
builtInsProvider = BuiltInsProvider.wrap(moduleDescriptor.builtIns),
modulesProvider = ModulesProvider.wrap(moduleDescriptor)
)
)
}
}
}
protected fun createEnvironment(bareModuleName: String): KotlinCoreEnvironment {
check(Name.isValidIdentifier(bareModuleName))
val configuration = newConfiguration()
configuration.put(CommonConfigurationKeys.MODULE_NAME, bareModuleName)
private fun getTestDataDir(): File {
val testCaseDir = lowercaseFirstLetter(
this::class.java.simpleName.substringBefore("FromSources").substringBefore("Test"),
true
)
val testDir = testDirectoryName
return KotlinCoreEnvironment.createForTests(testRootDisposable, configuration, EnvironmentConfigFiles.METADATA_CONFIG_FILES)
return File(getHomeDirectory())
.resolve("native/commonizer/testData")
.resolve(testCaseDir)
.resolve(testDir)
.also(::assertIsDirectory)
}
protected val KotlinCoreEnvironment.moduleName: Name
get() {
val bareModuleName = configuration.getNotNull(CommonConfigurationKeys.MODULE_NAME)
return Name.special("<$bareModuleName>")
}
protected val testDataDir: File
get() {
val testCaseDir = lowercaseFirstLetter(
this::class.java.simpleName.substringBefore("FromSources").substringBefore("Test"),
true
)
val testDir = testDirectoryName
return File(getHomeDirectory())
.resolve("native/commonizer/testData")
.resolve(testCaseDir)
.resolve(testDir)
.also(::assertIsDirectory)
}
protected val sourceModuleRoots: Triple<Set<File>, Set<File>, File>
get() {
val testDataDir = testDataDir
val originalRoots = testDataDir
.resolve("original")
.also(::assertIsDirectory)
.listFiles()
?.toSet()
?.also { it.forEach(::assertIsDirectory) }
val commonizedRoots = testDataDir
.resolve("commonized")
.also(::assertIsDirectory)
.listFiles()
?.toSet()
?.also { it.forEach(::assertIsDirectory) }
val commonRoot = commonizedRoots?.singleOrNull { it.name == "common" }
check(
!originalRoots.isNullOrEmpty() && !commonizedRoots.isNullOrEmpty() && commonRoot != null
&& (originalRoots + commonRoot).map { it.name }.toSet() == commonizedRoots.map { it.name }.toSet()
) {
"Source module misconfiguration in $testDataDir"
}
return Triple(originalRoots, commonizedRoots - commonRoot, commonRoot)
}
protected val sourceModuleDescriptors: Pair<Map<InputTarget, ModuleDescriptor>, Map<Target, ModuleDescriptor>>
get() {
fun analyzeTarget(targetRoot: File): ModuleDescriptor {
val environment = createEnvironment(targetRoot.parentFile.parentFile.name)
val psiFactory = KtPsiFactory(environment.project)
val psiFiles = targetRoot.walkTopDown()
.filter { it.isFile }
.map { psiFactory.createFile(it.name, doLoadFile(it)) }
.toList()
return CommonResolverForModuleFactory.analyzeFiles(
files = psiFiles,
moduleName = environment.moduleName,
dependOnBuiltIns = true,
languageVersionSettings = environment.configuration.languageVersionSettings,
targetPlatform = CommonPlatforms.defaultCommonPlatform
) { content ->
environment.createPackagePartProvider(content.moduleContentScope)
}.moduleDescriptor
}
val originalModules = sourceModuleRoots.first
.map { InputTarget(it.name) to analyzeTarget(it) }
.toMap()
val commonizedModules = sourceModuleRoots.second
.map { InputTarget(it.name) to analyzeTarget(it) }
.toMap()
val commonModule = OutputTarget(commonizedModules.keys) to analyzeTarget(sourceModuleRoots.third)
return originalModules to commonizedModules + commonModule
}
protected fun doTestSuccessfulCommonization() {
val (originalModules, commonizedModules) = sourceModuleDescriptors
val sourceModuleRoots: SourceModuleRoots = SourceModuleRoots.load(getTestDataDir())
val analyzedModules: AnalyzedModules = AnalyzedModules.create(sourceModuleRoots, testRootDisposable)
val result = runCommonization(originalModules.toCommonizationParameters())
val result: Result = runCommonization(analyzedModules.toCommonizationParameters())
assertCommonizationPerformed(result)
val commonTarget = commonizedModules.keys.filterIsInstance<OutputTarget>().single()
val commonTarget: OutputTarget = analyzedModules.commonizedCommonModule.target
assertEquals(commonTarget, result.commonTarget)
val commonModuleAsExpected = commonizedModules.getValue(commonTarget)
val commonModuleByCommonizer = result.modulesByTargets.getValue(commonTarget).single()
val commonModuleAsExpected: ModuleDescriptor = analyzedModules.commonizedCommonModule.module
val commonModuleByCommonizer: ModuleDescriptor = result.modulesByTargets.getValue(commonTarget).single()
assertValidModule(commonModuleAsExpected)
assertValidModule(commonModuleByCommonizer)
assertModulesAreEqual(commonModuleAsExpected, commonModuleByCommonizer, "\"$commonTarget\" target")
val concreteTargets = commonizedModules.keys - commonTarget
val concreteTargets: Set<InputTarget> = analyzedModules.commonizedPlatformModules.keys
assertEquals(concreteTargets, result.concreteTargets)
for (target in concreteTargets) {
val targetModuleAsExpected = commonizedModules.getValue(target)
val targetModuleByCommonizer = result.modulesByTargets.getValue(target).single()
val targetModuleAsExpected: ModuleDescriptor = analyzedModules.commonizedPlatformModules.getValue(target).module
val targetModuleByCommonizer: ModuleDescriptor = result.modulesByTargets.getValue(target).single()
assertValidModule(targetModuleAsExpected)
assertValidModule(targetModuleByCommonizer)
@@ -173,3 +84,178 @@ abstract class AbstractCommonizationFromSourcesTest : KtUsefulTestCase() {
}
}
}
private const val COMMON_TARGET_NAME = "common"
private data class SourceModuleRoot(
val targetName: String,
val root: File
) {
init {
assertIsDirectory(root)
}
companion object {
fun load(directory: File): SourceModuleRoot = SourceModuleRoot(
targetName = directory.name,
root = directory
)
}
}
private class SourceModuleRoots(
val originalPlatformRoots: Map<String, SourceModuleRoot>,
val commonizedPlatformRoots: Map<String, SourceModuleRoot>,
val commonizedCommonRoot: SourceModuleRoot
) {
init {
check(originalPlatformRoots.isNotEmpty())
check(COMMON_TARGET_NAME !in originalPlatformRoots)
check(originalPlatformRoots.keys == commonizedPlatformRoots.keys)
check(commonizedCommonRoot.targetName == COMMON_TARGET_NAME)
}
companion object {
fun load(dataDir: File): SourceModuleRoots = try {
val originalRoots = listRoots(dataDir, ORIGINAL_ROOTS_DIR)
val commonizedRoots = listRoots(dataDir, COMMONIZED_ROOTS_DIR)
SourceModuleRoots(
originalPlatformRoots = originalRoots,
commonizedPlatformRoots = commonizedRoots - COMMON_TARGET_NAME,
commonizedCommonRoot = commonizedRoots.getValue(COMMON_TARGET_NAME)
)
} catch (e: Exception) {
fail("Source module misconfiguration in $dataDir", cause = e)
}
private const val ORIGINAL_ROOTS_DIR = "original"
private const val COMMONIZED_ROOTS_DIR = "commonized"
private fun listRoots(dataDir: File, rootsDirName: String): Map<String, SourceModuleRoot> =
dataDir.resolve(rootsDirName).listFiles()?.toSet().orEmpty().map(SourceModuleRoot::load).associateBy { it.targetName }
}
}
private class AnalyzedModule<T : Target>(
val target: T,
val module: ModuleDescriptor
) {
companion object {
fun <T : Target> create(
target: T,
sourceModuleRoot: SourceModuleRoot,
parentDisposable: Disposable
): AnalyzedModule<T> {
val moduleName: String = sourceModuleRoot.root.parentFile.parentFile.name
check(Name.isValidIdentifier(moduleName))
return AnalyzedModule(
target = target,
module = analyze(
moduleName = moduleName,
moduleRoot = sourceModuleRoot.root,
parentDisposable = parentDisposable
)
)
}
private fun analyze(
moduleName: String,
moduleRoot: File,
parentDisposable: Disposable
): ModuleDescriptor {
val configuration: CompilerConfiguration = newConfiguration()
configuration.put(CommonConfigurationKeys.MODULE_NAME, moduleName)
val environment: KotlinCoreEnvironment = KotlinCoreEnvironment.createForTests(
parentDisposable = parentDisposable,
initialConfiguration = configuration,
extensionConfigs = EnvironmentConfigFiles.METADATA_CONFIG_FILES
)
val psiFactory = KtPsiFactory(environment.project)
val psiFiles: List<KtFile> = moduleRoot.walkTopDown()
.filter { it.isFile }
.map { psiFactory.createFile(it.name, doLoadFile(it)) }
.toList()
return CommonResolverForModuleFactory.analyzeFiles(
files = psiFiles,
moduleName = Name.special("<$moduleName>"),
dependOnBuiltIns = true,
languageVersionSettings = environment.configuration.languageVersionSettings,
targetPlatform = CommonPlatforms.defaultCommonPlatform
) { content ->
environment.createPackagePartProvider(content.moduleContentScope)
}.moduleDescriptor
}
}
}
private class AnalyzedModules(
val originalPlatformModules: Map<InputTarget, AnalyzedModule<InputTarget>>,
val commonizedPlatformModules: Map<InputTarget, AnalyzedModule<InputTarget>>,
val commonizedCommonModule: AnalyzedModule<OutputTarget>
) {
init {
check(originalPlatformModules.isNotEmpty())
check(originalPlatformModules.keys == commonizedPlatformModules.keys)
}
fun toCommonizationParameters(): Parameters = originalPlatformModules.mapValues { it.value.module }.toCommonizationParameters()
companion object {
fun create(
sourceModuleRoots: SourceModuleRoots,
parentDisposable: Disposable
): AnalyzedModules {
val originalPlatformModules: Map<InputTarget, AnalyzedModule<InputTarget>> = createInputTargetModules(
sourceModuleRoots = sourceModuleRoots.originalPlatformRoots,
parentDisposable = parentDisposable
)
val commonizedCommonModule: AnalyzedModule<OutputTarget> = AnalyzedModule.create(
target = OutputTarget(originalPlatformModules.keys),
sourceModuleRoot = sourceModuleRoots.commonizedCommonRoot,
parentDisposable = parentDisposable
)
val commonizedPlatformModules: Map<InputTarget, AnalyzedModule<InputTarget>> = createInputTargetModules(
sourceModuleRoots = sourceModuleRoots.commonizedPlatformRoots,
parentDisposable = parentDisposable
)
return AnalyzedModules(
originalPlatformModules = originalPlatformModules,
commonizedPlatformModules = commonizedPlatformModules,
commonizedCommonModule = commonizedCommonModule
)
}
private fun createInputTargetModules(
sourceModuleRoots: Map<String, SourceModuleRoot>,
parentDisposable: Disposable
): Map<InputTarget, AnalyzedModule<InputTarget>> = sourceModuleRoots.map { (targetName, sourceModuleRoot) ->
AnalyzedModule.create(
target = InputTarget(targetName),
sourceModuleRoot = sourceModuleRoot,
parentDisposable = parentDisposable
)
}.associateBy { it.target }
}
}
private fun Map<InputTarget, ModuleDescriptor>.toCommonizationParameters(): Parameters = Parameters().also {
forEach { (target, moduleDescriptor) ->
it.addTarget(
TargetProvider(
target = target,
builtInsClass = moduleDescriptor.builtIns::class.java,
builtInsProvider = BuiltInsProvider.wrap(moduleDescriptor.builtIns),
modulesProvider = ModulesProvider.wrap(moduleDescriptor)
)
)
}
}