Wizard: always use all plugin set in tests

This commit is contained in:
Ilya Kirillov
2020-02-20 19:32:34 +03:00
parent 7a3d730aec
commit 8259bf749d
23 changed files with 67 additions and 174 deletions
@@ -21,7 +21,7 @@ dependencies {
android {
compileSdkVersion 29
defaultConfig {
applicationId 'testArtifactId'
applicationId 'testGroupId.android'
minSdkVersion 24
targetSdkVersion 29
versionCode 1
@@ -21,7 +21,7 @@ dependencies {
android {
compileSdkVersion(29)
defaultConfig {
applicationId = "testArtifactId"
applicationId = "testGroupId.android"
minSdkVersion(24)
targetSdkVersion(29)
versionCode = 1
@@ -1,3 +0,0 @@
org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.KotlinPlugin
org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.gradle.GroovyDslPlugin
org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.gradle.KotlinDslPlugin
@@ -1,3 +1,5 @@
android:
androidSdkPath: androidSdkPath
kotlin:
projectKind: Multiplatform
modules:
@@ -1,3 +0,0 @@
org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.KotlinPlugin
org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.gradle.GroovyDslPlugin
org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.gradle.KotlinDslPlugin
@@ -1,3 +0,0 @@
org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.KotlinPlugin
org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.gradle.GroovyDslPlugin
org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.gradle.KotlinDslPlugin
@@ -1,5 +0,0 @@
org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.KotlinPlugin
org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.MavenPlugin
org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.gradle.GroovyDslPlugin
org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.gradle.KotlinDslPlugin
@@ -1,3 +0,0 @@
org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.KotlinPlugin
org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.gradle.GroovyDslPlugin
org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.gradle.KotlinDslPlugin
@@ -1,3 +0,0 @@
org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.KotlinPlugin
org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.gradle.GroovyDslPlugin
org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.gradle.KotlinDslPlugin
@@ -1,3 +0,0 @@
org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.KotlinPlugin
org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.gradle.GroovyDslPlugin
org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.gradle.KotlinDslPlugin
@@ -5,11 +5,13 @@
package org.jetbrains.kotlin.tools.projectWizard.cli
import com.intellij.testFramework.UsefulTestCase
import org.jetbrains.kotlin.tools.projectWizard.core.ExceptionError
import org.jetbrains.kotlin.tools.projectWizard.core.div
import org.jetbrains.kotlin.tools.projectWizard.core.onFailure
import org.jetbrains.kotlin.tools.projectWizard.core.service.Services
import org.jetbrains.kotlin.tools.projectWizard.phases.GenerationPhase
import org.jetbrains.kotlin.tools.projectWizard.plugins.Plugins
import org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.MavenPlugin
import org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.gradle.GroovyDslPlugin
import org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.gradle.KotlinDslPlugin
@@ -18,27 +20,24 @@ import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
abstract class AbstractBuildFileGenerationTest : AbstractPluginBasedTest() {
abstract class AbstractBuildFileGenerationTest : UsefulTestCase() {
fun doTest(directoryPath: String) {
val directory = Paths.get(directoryPath)
val testData = init(directory)
if (KotlinDslPlugin::class in testData.pluginClasses) {
doTest(directory, testData, BuildSystem.GRADLE_KOTLIN_DSL)
}
if (GroovyDslPlugin::class in testData.pluginClasses) {
doTest(directory, testData, BuildSystem.GRADLE_GROOVY_DSL)
}
if (MavenPlugin::class in testData.pluginClasses) {
doTest(directory, testData, BuildSystem.MAVEN)
val expectedDirectory = expectedDirectory(directory)
for (buildSystem in BuildSystem.values()) {
if (Files.exists(expectedDirectory / buildSystem.buildFileName)) {
doTest(directory, buildSystem)
}
}
}
private fun doTest(directory: Path, testData: WizardTestData, buildSystem: BuildSystem) {
private fun doTest(directory: Path, buildSystem: BuildSystem) {
val yaml = directory.resolve("settings.yaml").toFile().readText() + "\n" +
defaultStructure + "\n" +
buildSystem.yaml
val tempDir = Files.createTempDirectory(null)
val wizard = YamlWizard(yaml, tempDir.toString(), testData.createPlugins, isUnitTestMode = true)
val wizard = YamlWizard(yaml, tempDir.toString(), Plugins.allPlugins, isUnitTestMode = true)
val result = wizard.apply(Services.IDEA_INDEPENDENT_SERVICES, GenerationPhase.ALL)
result.onFailure { errors ->
errors.forEach { error ->
@@ -49,7 +48,7 @@ abstract class AbstractBuildFileGenerationTest : AbstractPluginBasedTest() {
fail(errors.joinToString("\n"))
}
val expectedDirectory = (directory / EXPECTED_DIRECTORY_NAME).takeIf { Files.exists(it) } ?: directory
val expectedDirectory = expectedDirectory(directory)
compareFiles(
expectedDirectory.allBuildFiles(buildSystem), expectedDirectory,
@@ -84,6 +83,9 @@ abstract class AbstractBuildFileGenerationTest : AbstractPluginBasedTest() {
)
}
private fun expectedDirectory(directory: Path): Path =
(directory / EXPECTED_DIRECTORY_NAME).takeIf { Files.exists(it) } ?: directory
companion object {
private const val EXPECTED_DIRECTORY_NAME = "expected"
@@ -1,45 +0,0 @@
/*
* Copyright 2010-2019 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.tools.projectWizard.cli
import com.intellij.testFramework.UsefulTestCase
import org.jetbrains.kotlin.tools.projectWizard.core.Context
import org.jetbrains.kotlin.tools.projectWizard.core.Plugin
import org.jetbrains.kotlin.tools.projectWizard.core.PluginReference
import org.jetbrains.kotlin.tools.projectWizard.plugins.StructurePlugin
import org.jetbrains.kotlin.tools.projectWizard.plugins.templates.TemplatesPlugin
import java.nio.file.Path
import kotlin.reflect.full.primaryConstructor
abstract class AbstractPluginBasedTest : UsefulTestCase() {
open val defaultPlugins: List<PluginReference> = listOf(
StructurePlugin::class,
TemplatesPlugin::class
)
protected fun init(directory: Path): WizardTestData {
val pluginNames = directory.resolve("plugins.txt").toFile().readLines().mapNotNull { name ->
name.trim().takeIf { it.isNotBlank() }
}.distinct()
@Suppress("UNCHECKED_CAST")
val pluginClasses = defaultPlugins + pluginNames.map { pluginName ->
Class.forName(pluginName).kotlin as PluginReference
}
val createPlugins = { context: Context ->
pluginClasses.map { pluginClass ->
pluginClass.primaryConstructor!!.call(context)
}
}
return WizardTestData(pluginClasses, createPlugins)
}
}
data class WizardTestData(
val pluginClasses: List<PluginReference>,
val createPlugins: (Context) -> List<Plugin>
)
@@ -1,68 +1,17 @@
package org.jetbrains.kotlin.tools.projectWizard.core
import org.jetbrains.kotlin.tools.projectWizard.core.entity.SettingReference
import org.jetbrains.kotlin.tools.projectWizard.core.entity.SettingType
typealias Checker = ReadingContext.() -> Boolean
val ALWAYS_AVAILABLE_CHECKER = checker { true }
sealed class CheckerRule {
abstract fun check(context: ReadingContext): Boolean
}
data class RuleBySettingValue(
val settingReference: SettingReference<Any, SettingType<Any>>,
val expectedValue: Any
) : CheckerRule() {
override fun check(context: ReadingContext): Boolean = with(context) {
settingReference.notRequiredSettingValue() == expectedValue
}
}
data class OrRule(
val left: CheckerRule,
val right: CheckerRule
) : CheckerRule() {
override fun check(context: ReadingContext): Boolean = left.check(context) || right.check(context)
}
data class Checker(val rules: List<CheckerRule>) {
fun check(context: ReadingContext) =
rules.all { rule -> rule.check(context) }
class Builder {
private val rules = mutableListOf<CheckerRule>()
infix fun <V : Any, T: SettingType<V>> SettingReference<V, T>.shouldBeEqual(value: V) =
RuleBySettingValue(this, value)
infix fun CheckerRule.or(other: CheckerRule) =
OrRule(this, other)
fun extend(parent: Checker) {
this.rules += parent.rules
}
fun rule(rule: CheckerRule) {
rules += rule
}
fun build() = Checker(rules)
}
companion object {
val ALWAYS_AVAILABLE = Checker(emptyList())
}
}
fun checker(init: Checker.Builder.() -> Unit) =
Checker.Builder().apply(init).build()
fun checker(check: Checker) = check
interface ContextOwner {
val context: Context
}
interface ActivityCheckerOwner {
val activityChecker: Checker
val isAvailable: Checker
fun isActive(readingContext: ReadingContext) = activityChecker.check(readingContext)
fun isActive(readingContext: ReadingContext) = isAvailable(readingContext)
}
@@ -177,7 +177,7 @@ data class InternalSetting<out V : Any, out T : SettingType<V>>(
override val path: String,
override val title: String,
override val defaultValue: V?,
override val activityChecker: Checker,
override val isAvailable: Checker,
override val isRequired: Boolean,
override val isSavable: Boolean,
override var neededAtPhase: GenerationPhase,
@@ -200,14 +200,16 @@ class TemplateSetting<out V : Any, out T : SettingType<V>>(
) : SettingImpl<V, T>(), Setting<V, T> by internal
abstract class SettingBuilder<V : Any, T : SettingType<V>>(
private val path: String,
private val title: String,
private val neededAtPhase: GenerationPhase
) {
var checker: Checker = Checker.ALWAYS_AVAILABLE
var isAvailable: ReadingContext.() -> Boolean = { true }
var defaultValue: V? = null
var isSavable: Boolean = false
var isRequired: Boolean? = null
protected var validator = SettingValidator<V> { ValidationResult.OK }
@@ -226,8 +228,8 @@ abstract class SettingBuilder<V : Any, T : SettingType<V>>(
path = path,
title = title,
defaultValue = defaultValue,
activityChecker = checker,
isRequired = defaultValue == null,
isAvailable = isAvailable,
isRequired = isRequired ?: (defaultValue == null),
isSavable = isSavable,
neededAtPhase = neededAtPhase,
validator = validator,
@@ -42,7 +42,7 @@ data class PipelineTask(
val before: List<PipelineTaskReference>,
val after: List<PipelineTaskReference>,
val phase: GenerationPhase,
val checker: Checker,
val isAvailable: Checker,
val title: String?
) : Task() {
class Builder(
@@ -53,7 +53,7 @@ data class PipelineTask(
private val before = mutableListOf<PipelineTaskReference>()
private val after = mutableListOf<PipelineTaskReference>()
var activityChecker: Checker = Checker.ALWAYS_AVAILABLE
var isAvailable: Checker = ALWAYS_AVAILABLE_CHECKER
var title: String? = null
@@ -69,7 +69,7 @@ data class PipelineTask(
this.after.addAll(after)
}
fun build(): PipelineTask = PipelineTask(name, action, before, after, phase, activityChecker, title)
fun build(): PipelineTask = PipelineTask(name, action, before, after, phase, isAvailable, title)
}
companion object {
@@ -8,11 +8,13 @@ package org.jetbrains.kotlin.tools.projectWizard.plugins
import org.jetbrains.kotlin.tools.projectWizard.core.Context
import org.jetbrains.kotlin.tools.projectWizard.core.Plugin
import org.jetbrains.kotlin.tools.projectWizard.core.UNIT_SUCCESS
import org.jetbrains.kotlin.tools.projectWizard.core.checker
import org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators.AndroidModuleConfigurator
import org.jetbrains.kotlin.tools.projectWizard.phases.GenerationPhase
import org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.allModules
import org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.allIRModules
import org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem.gradle.GradlePlugin
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.KotlinPlugin
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.withAllSubModules
class AndroidPlugin(context: Context) : Plugin(context) {
val androidSdkPath by pathSetting(
@@ -20,15 +22,22 @@ class AndroidPlugin(context: Context) : Plugin(context) {
neededAtPhase = GenerationPhase.PROJECT_GENERATION
) {
isSavable = true
isAvailable = isAndroidContainingProject
shouldExists()
}
private val isAndroidContainingProject = checker {
KotlinPlugin::modules.settingValue
.withAllSubModules(includeSourcesets = true)
.any { it.configurator is AndroidModuleConfigurator }
}
val addAndroidSdkToLocalProperties by pipelineTask(GenerationPhase.PROJECT_GENERATION) {
runBefore(GradlePlugin::createLocalPropertiesFile)
runAfter(KotlinPlugin::createModules)
isAvailable = isAndroidContainingProject
withAction {
if (allModules.none { it.originalModule.configurator is AndroidModuleConfigurator }) return@withAction UNIT_SUCCESS
if (allIRModules.none { it.originalModule.configurator is AndroidModuleConfigurator }) return@withAction UNIT_SUCCESS
val path = AndroidPlugin::androidSdkPath.settingValue
GradlePlugin::localProperties.addValues(
"sdk.dir" to path
@@ -86,13 +86,12 @@ abstract class BuildSystemPlugin(context: Context) : Plugin(context) {
withAction {
val data = BuildSystemPlugin::buildSystemData.propertyValue.first { it.type == buildSystemType }
service<ProjectImportingWizardService> { service -> service.isSuitableFor(data.type) }!!
.importProject(StructurePlugin::projectPath.reference.settingValue, allModules)
.importProject(StructurePlugin::projectPath.reference.settingValue, allIRModules)
}
}
protected fun addBuildSystemData(data: BuildSystemData) = pipelineTask(GenerationPhase.PREPARE) {
runBefore(BuildSystemPlugin::createModules)
activityChecker = Checker.ALWAYS_AVAILABLE
withAction {
BuildSystemPlugin::buildSystemData.addValues(data)
}
@@ -125,7 +124,7 @@ val BuildSystemType.isGradle
get() = this == BuildSystemType.GradleGroovyDsl
|| this == BuildSystemType.GradleKotlinDsl
val WritingContext.allModules
val ReadingContext.allIRModules
get() = BuildSystemPlugin::buildFiles.propertyValue.flatMap { buildFile ->
buildFile.modules.modules
}
@@ -3,7 +3,6 @@ package org.jetbrains.kotlin.tools.projectWizard.plugins.buildSystem
import org.jetbrains.kotlin.tools.projectWizard.core.Context
import org.jetbrains.kotlin.tools.projectWizard.core.asSuccess
import org.jetbrains.kotlin.tools.projectWizard.core.checker
import org.jetbrains.kotlin.tools.projectWizard.core.entity.reference
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.RootFileModuleStructureIR
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle.ModulesDependencyMavenIR
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.maven.PluginRepositoryMavenIR
@@ -17,13 +16,13 @@ class MavenPlugin(context: Context) : BuildSystemPlugin(context) {
override val title: String = "Maven"
private val isMaven = checker {
rule(BuildSystemPlugin::type.reference shouldBeEqual BuildSystemType.Maven)
BuildSystemPlugin::type.settingValue == BuildSystemType.Maven
}
val createSettingsFileTask by pipelineTask(GenerationPhase.PROJECT_GENERATION) {
runAfter(KotlinPlugin::createModules)
runBefore(BuildSystemPlugin::createModules)
activityChecker = isMaven
isAvailable = isMaven
withAction {
BuildSystemPlugin::buildFiles.update { buildFiles ->
if (buildFiles.size == 1) return@update buildFiles.asSuccess()
@@ -45,7 +44,7 @@ class MavenPlugin(context: Context) : BuildSystemPlugin(context) {
val addBuildSystemPluginRepositories by pipelineTask(GenerationPhase.PROJECT_GENERATION) {
runAfter(KotlinPlugin::createPluginRepositories)
runBefore(BuildSystemPlugin::createModules)
activityChecker = isMaven
isAvailable = isMaven
withAction {
val repositories = BuildSystemPlugin::pluginRepositoreis.propertyValue
@@ -23,13 +23,13 @@ import org.jetbrains.kotlin.tools.projectWizard.templates.FileTemplateDescriptor
abstract class GradlePlugin(context: Context) : BuildSystemPlugin(context) {
val createGradleWrapper by booleanSetting("Create Gradle Wrapper", GenerationPhase.FIRST_STEP) {
defaultValue = true
checker = isGradle
isAvailable = isGradle
}
val version by versionSetting("Gradle Version", GenerationPhase.FIRST_STEP) {
defaultValue = defaultVersions.first()
checker = isGradle
isAvailable = isGradle
}
val gradleVersions by property<List<Version>>(emptyList())
@@ -39,7 +39,7 @@ abstract class GradlePlugin(context: Context) : BuildSystemPlugin(context) {
val createGradlePropertiesFile by pipelineTask(GenerationPhase.PROJECT_GENERATION) {
runAfter(KotlinPlugin::createModules)
runBefore(TemplatesPlugin::renderFileTemplates)
activityChecker = isGradle
isAvailable = isGradle
withAction {
TemplatesPlugin::addFileTemplate.execute(
FileTemplate(
@@ -63,7 +63,7 @@ abstract class GradlePlugin(context: Context) : BuildSystemPlugin(context) {
val createLocalPropertiesFile by pipelineTask(GenerationPhase.PROJECT_GENERATION) {
runAfter(KotlinPlugin::createModules)
runBefore(TemplatesPlugin::renderFileTemplates)
activityChecker = isGradle
isAvailable = isGradle
withAction {
TemplatesPlugin::addFileTemplate.execute(
FileTemplate(
@@ -80,17 +80,12 @@ abstract class GradlePlugin(context: Context) : BuildSystemPlugin(context) {
}
}
private val isGradle = checker {
rule(
(BuildSystemPlugin::type.reference shouldBeEqual BuildSystemType.GradleKotlinDsl) or
(BuildSystemPlugin::type.reference shouldBeEqual BuildSystemType.GradleGroovyDsl)
)
}
private val isGradle = checker { buildSystemType.isGradle }
val initGradleWrapperTask by pipelineTask(GenerationPhase.PROJECT_GENERATION) {
runBefore(TemplatesPlugin::renderFileTemplates)
activityChecker = isGradle
isAvailable = isGradle
withAction {
if (!GradlePlugin::createGradleWrapper.reference.settingValue) return@withAction UNIT_SUCCESS
TemplatesPlugin::addFileTemplate.execute(
@@ -111,7 +106,7 @@ abstract class GradlePlugin(context: Context) : BuildSystemPlugin(context) {
val createSettingsFileTask by pipelineTask(GenerationPhase.PROJECT_GENERATION) {
runAfter(KotlinPlugin::createPluginRepositories)
activityChecker = isGradle
isAvailable = isGradle
withAction {
val (createBuildFile, buildFileName) = settingsGradleBuildFileData ?: return@withAction UNIT_SUCCESS
@@ -1,6 +1,7 @@
package org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin
import org.jetbrains.kotlin.tools.projectWizard.core.*
import org.jetbrains.kotlin.tools.projectWizard.core.context.ReadingContext
import org.jetbrains.kotlin.tools.projectWizard.core.entity.*
import org.jetbrains.kotlin.tools.projectWizard.core.service.FileSystemWizardService
import org.jetbrains.kotlin.tools.projectWizard.core.service.KotlinVersionProviderService
@@ -130,11 +131,11 @@ enum class ProjectKind(override val text: String) : DisplayableSettingItem {
fun List<Module>.withAllSubModules(includeSourcesets: Boolean = false): List<Module> = buildList {
fun handleModule(module: Module) {
+module
if (module.kind == ModuleKind.singleplatformJvm
if (module.kind != ModuleKind.multiplatform
|| includeSourcesets && module.kind == ModuleKind.multiplatform
) {
module.subModules.forEach(::handleModule)
}
}
forEach(::handleModule)
}
}
@@ -1,6 +1,7 @@
package org.jetbrains.kotlin.tools.projectWizard.plugins.projectTemplates
import org.jetbrains.kotlin.tools.projectWizard.core.*
import org.jetbrains.kotlin.tools.projectWizard.core.entity.reference
import org.jetbrains.kotlin.tools.projectWizard.phases.GenerationPhase
import org.jetbrains.kotlin.tools.projectWizard.projectTemplates.CustomMultiplatformProjectTemplate
import org.jetbrains.kotlin.tools.projectWizard.projectTemplates.ProjectTemplate
@@ -13,12 +14,13 @@ class ProjectTemplatesPlugin(context: Context) : Plugin(context) {
CustomMultiplatformProjectTemplate
}) {
values = ProjectTemplate.ALL
isRequired = false
}
val initTemplate by pipelineTask(GenerationPhase.INIT_TEMPLATE) {
withAction {
val selectedTemplate = ProjectTemplatesPlugin::template.settingValue
selectedTemplate.setsValues.forEach { (setting, value) ->
val selectedTemplate = ProjectTemplatesPlugin::template.reference.notRequiredSettingValue
selectedTemplate?.setsValues?.forEach { (setting, value) ->
context.settingContext[setting] = value
}
UNIT_SUCCESS
@@ -33,6 +33,7 @@ abstract class Wizard(createPlugins: PluginsCreator, val servicesManager: Servic
for (setting in pluginSettings) {
if (setting.neededAtPhase !in phases) continue
if (!setting.isSavable) continue
if (!setting.isAvailable(valuesReadingContext)) continue
val serializer = setting.type.serializer as? SerializerImpl<Any> ?: continue
service<SettingSavingWizardService>().saveSettingValue(
setting.path,
@@ -58,7 +59,7 @@ abstract class Wizard(createPlugins: PluginsCreator, val servicesManager: Servic
.distinctBy { it.path }
.asSequence()
.filter { task -> task.phase in phases }
.filter { task -> task.checker.check(taskRunningContext) }
.filter { task -> task.isAvailable(taskRunningContext) }
.map { task -> onTaskExecuting(task); task.action(taskRunningContext) }
.sequenceFailFirst()
.ignore()
@@ -15,7 +15,7 @@ class TaskSorterTest {
before = emptyList(),
after = emptyList(),
phase = GenerationPhase.FIRST_STEP,
checker = Checker.ALWAYS_AVAILABLE,
isAvailable = ALWAYS_AVAILABLE_CHECKER,
title = null
)
}