blob: 28ee4f8d79ef83a03f32f862128519fc530afcaf [file]
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.plugins.gradle.importing
import com.intellij.ide.impl.NewProjectUtil
import com.intellij.ide.projectWizard.NewProjectWizard
import com.intellij.ide.projectWizard.ProjectTypeStep
import com.intellij.ide.util.newProjectWizard.AbstractProjectWizard
import com.intellij.ide.util.projectWizard.ModuleWizardStep
import com.intellij.openapi.Disposable
import com.intellij.openapi.application.invokeAndWaitIfNeeded
import com.intellij.openapi.application.runWriteAction
import com.intellij.openapi.externalSystem.model.project.ProjectData
import com.intellij.openapi.externalSystem.service.project.manage.ProjectDataImportListener
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil.findProjectData
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil.getSettings
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.ui.configuration.DefaultModulesProvider
import com.intellij.openapi.roots.ui.configuration.ModulesProvider
import com.intellij.openapi.roots.ui.configuration.actions.NewModuleAction
import com.intellij.openapi.util.Disposer
import com.intellij.openapi.util.text.StringUtil.convertLineSeparators
import com.intellij.testFramework.PlatformTestUtil
import org.jetbrains.concurrency.AsyncPromise
import org.jetbrains.plugins.gradle.org.jetbrains.plugins.gradle.util.ProjectInfoBuilder
import org.jetbrains.plugins.gradle.org.jetbrains.plugins.gradle.util.ProjectInfoBuilder.ModuleInfo
import org.jetbrains.plugins.gradle.org.jetbrains.plugins.gradle.util.ProjectInfoBuilder.ProjectInfo
import org.jetbrains.plugins.gradle.service.project.wizard.GradleFrameworksWizardStep
import org.jetbrains.plugins.gradle.service.project.wizard.GradleStructureWizardStep
import org.jetbrains.plugins.gradle.settings.GradleSettings
import org.jetbrains.plugins.gradle.util.GradleConstants
import org.junit.runners.Parameterized
import java.io.File
import java.util.concurrent.TimeUnit
import com.intellij.openapi.externalSystem.util.use as utilUse
abstract class GradleCreateProjectTestCase : GradleImportingTestCase() {
fun Project.assertProjectStructure(projectInfo: ProjectInfo) {
val rootModule = projectInfo.rootModule.ideName
val rootSourceSetModules = projectInfo.rootModule.modulesPerSourceSet
val modules = projectInfo.modules.map { it.ideName }
val sourceSetModules = projectInfo.modules.flatMap { it.modulesPerSourceSet }
assertModules(this, rootModule, *rootSourceSetModules.toTypedArray(), *modules.toTypedArray(), *sourceSetModules.toTypedArray())
}
fun deleteProject(projectInfo: ProjectInfo) {
invokeAndWaitIfNeeded {
runWriteAction {
for (module in projectInfo.modules) {
val root = module.root
if (root.exists()) {
root.delete(null)
}
}
}
}
}
fun projectInfo(id: String, useKotlinDsl: Boolean = false, configure: ProjectInfoBuilder.() -> Unit): ProjectInfo {
return ProjectInfoBuilder.projectInfo(id, myProjectRoot) {
this.useKotlinDsl = useKotlinDsl
configure()
}
}
protected fun Project.assertDefaultProjectSettings() {
val externalProjectPath = basePath!!
val settings = getSettings(this, GradleConstants.SYSTEM_ID) as GradleSettings
val projectSettings = settings.getLinkedProjectSettings(externalProjectPath)!!
assertEquals(projectSettings.externalProjectPath, externalProjectPath)
assertEquals(projectSettings.isUseQualifiedModuleNames, true)
assertEquals(settings.storeProjectFilesExternally, true)
}
fun withProject(projectInfo: ProjectInfo, save: Boolean = false, action: Project.() -> Unit) {
createProject(projectInfo).use(save = save) { project ->
for (moduleInfo in projectInfo.modules) {
createModule(moduleInfo, project)
}
project.action()
}
}
private fun createProject(projectInfo: ProjectInfo): Project {
return createProject(projectInfo.rootModule.root.path) { step ->
configureWizardStepSettings(step, projectInfo.rootModule, null)
}
}
private fun createModule(moduleInfo: ModuleInfo, project: Project) {
val parentData = findProjectData(project, GradleConstants.SYSTEM_ID, project.basePath!!)!!
return createModule(moduleInfo.root.path, project) { step ->
configureWizardStepSettings(step, moduleInfo, parentData.data)
}
}
private fun configureWizardStepSettings(step: ModuleWizardStep, moduleInfo: ModuleInfo, parentData: ProjectData?) {
when (step) {
is ProjectTypeStep -> {
step.setSelectedTemplate("Gradle", null)
val frameworksStep = step.frameworksStep
frameworksStep as GradleFrameworksWizardStep
frameworksStep.setUseKotlinDsl(moduleInfo.useKotlinDsl)
}
is GradleStructureWizardStep -> {
step.parentData = parentData
moduleInfo.groupId?.let { step.groupId = it }
step.artifactId = moduleInfo.artifactId
moduleInfo.version?.let { step.version = it }
step.entityName = moduleInfo.simpleName
step.location = moduleInfo.root.path
}
}
}
private fun createProject(directory: String, configure: (ModuleWizardStep) -> Unit): Project {
val project = invokeAndWaitIfNeeded {
val wizard = createWizard(null, directory)
wizard.runWizard(configure)
NewProjectUtil.createFromWizard(wizard, null)
}
waitForImportCompletion(project)
return project
}
private fun createModule(directory: String, project: Project, configure: (ModuleWizardStep) -> Unit) {
invokeAndWaitIfNeeded {
val wizard = createWizard(project, directory)
wizard.runWizard(configure)
NewModuleAction().createModuleFromWizard(project, null, wizard)
}
waitForImportCompletion(project)
}
private fun createWizard(project: Project?, directory: String): AbstractProjectWizard {
val modulesProvider = project?.let { DefaultModulesProvider(it) } ?: ModulesProvider.EMPTY_MODULES_PROVIDER
val projectWizard = NewProjectWizard(project, modulesProvider, directory).also {
PlatformTestUtil.dispatchAllInvocationEventsInIdeEventQueue()
}
Disposer.register(testRootDisposable, Disposable { invokeAndWaitIfNeeded { Disposer.dispose(projectWizard.disposable) } })
return projectWizard
}
private fun AbstractProjectWizard.runWizard(configure: ModuleWizardStep.() -> Unit) {
while (true) {
val currentStep = currentStepObject
currentStep.configure()
if (isLast) break
doNextAction()
if (currentStep === currentStepObject) {
throw RuntimeException("$currentStepObject is not validated")
}
}
if (!doFinishAction()) {
throw RuntimeException("$currentStepObject is not validated")
}
}
private fun waitForImportCompletion(project: Project) {
val promise = AsyncPromise<Any>()
val connection = project.messageBus.connect()
connection.subscribe(ProjectDataImportListener.TOPIC, ProjectDataImportListener { promise.setResult(null) })
invokeAndWaitIfNeeded { PlatformTestUtil.waitForPromise(promise, TimeUnit.MINUTES.toMillis(1)) }
}
fun assertSettingsFileContent(projectInfo: ProjectInfo) {
val builder = StringBuilder()
val rootModuleInfo = projectInfo.rootModule
val useKotlinDsl = rootModuleInfo.useKotlinDsl
builder.appendln(defineProject(rootModuleInfo.artifactId, useKotlinDsl))
for (moduleInfo in projectInfo.modules) {
val externalName = moduleInfo.externalName
val artifactId = moduleInfo.artifactId
when (moduleInfo.isFlat) {
true -> builder.appendln(includeFlatModule(externalName, useKotlinDsl))
else -> builder.appendln(includeModule(externalName, useKotlinDsl))
}
if (externalName != artifactId) {
builder.appendln(renameModule(externalName, artifactId, useKotlinDsl))
}
}
val settingsFileName = getSettingsFileName(useKotlinDsl)
val settingsFile = File(rootModuleInfo.root.path, settingsFileName)
assertFileContent(settingsFile, builder.toString())
}
private fun assertFileContent(file: File, content: String) {
val expected = convertLineSeparators(file.readText().trim())
val actual = convertLineSeparators(content.trim())
assertEquals(expected, actual)
}
fun assertBuildScriptFiles(projectInfo: ProjectInfo) {
for (module in projectInfo.modules + projectInfo.rootModule) {
val buildFileName = getBuildFileName(module.useKotlinDsl)
val buildFile = File(module.root.path, buildFileName)
assertTrue(buildFile.exists())
}
}
private fun getBuildFileName(useKotlinDsl: Boolean): String {
return when (useKotlinDsl) {
true -> """build.gradle.kts"""
else -> """build.gradle"""
}
}
private fun getSettingsFileName(useKotlinDsl: Boolean): String {
return when (useKotlinDsl) {
true -> """settings.gradle.kts"""
else -> """settings.gradle"""
}
}
private fun defineProject(name: String, useKotlinDsl: Boolean): String {
return when (useKotlinDsl) {
true -> """rootProject.name = "$name""""
else -> """rootProject.name = '$name'"""
}
}
private fun includeModule(name: String, useKotlinDsl: Boolean): String {
return when (useKotlinDsl) {
true -> """include("$name")"""
else -> """include '$name'"""
}
}
private fun includeFlatModule(name: String, useKotlinDsl: Boolean): String {
return when (useKotlinDsl) {
true -> """includeFlat("$name")"""
else -> """includeFlat '$name'"""
}
}
private fun renameModule(from: String, to: String, useKotlinDsl: Boolean): String {
return when (useKotlinDsl) {
true -> """findProject(":$from")?.name = "$to""""
else -> """findProject(':$from')?.name = '$to'"""
}
}
fun Project.use(save: Boolean = false, action: (Project) -> Unit) {
utilUse(save) {
try {
action(this)
}
finally {
GradleSetupProjectTest.removeGradleJvmSdk(this)
}
}
}
companion object {
/**
* It's sufficient to run the test against one gradle version
*/
@Parameterized.Parameters(name = "with Gradle-{0}")
@JvmStatic
fun tests(): Collection<Array<out String>> = arrayListOf(arrayOf(BASE_GRADLE_VERSION))
}
}