diff --git a/plugins/gradle/java/testSources/importing/GradlePartialImportingTest.kt b/plugins/gradle/java/testSources/importing/GradlePartialImportingTest.kt index 1bf4d5e77844..e016a1569974 100644 --- a/plugins/gradle/java/testSources/importing/GradlePartialImportingTest.kt +++ b/plugins/gradle/java/testSources/importing/GradlePartialImportingTest.kt @@ -1,4 +1,4 @@ -// 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. +// Copyright 2000-2020 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.openapi.externalSystem.importing.ImportSpecBuilder @@ -7,7 +7,10 @@ import com.intellij.openapi.externalSystem.service.project.ProjectDataManager import com.intellij.openapi.externalSystem.test.ExternalSystemTestUtil.assertMapsEqual import com.intellij.openapi.externalSystem.util.ExternalSystemUtil import com.intellij.openapi.progress.ProcessCanceledException +import com.intellij.openapi.util.io.FileUtil.pathsEqual import com.intellij.testFramework.registerServiceInstance +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Condition import org.gradle.tooling.BuildController import org.gradle.tooling.model.BuildModel import org.gradle.tooling.model.Model @@ -19,6 +22,8 @@ import org.jetbrains.plugins.gradle.model.Project import org.jetbrains.plugins.gradle.model.ProjectImportAction import org.jetbrains.plugins.gradle.model.ProjectImportModelProvider import org.jetbrains.plugins.gradle.service.project.* +import org.jetbrains.plugins.gradle.tooling.annotation.TargetVersions +import org.jetbrains.plugins.gradle.tooling.builder.ProjectPropertiesTestModelBuilder.ProjectProperties import org.jetbrains.plugins.gradle.util.GradleConstants.SYSTEM_ID import org.junit.Test import java.io.Serializable @@ -28,8 +33,8 @@ class GradlePartialImportingTest : BuildViewMessagesImportingTestCase() { override fun setUp() { super.setUp() myProject.registerServiceInstance(ModelConsumer::class.java, ModelConsumer()) - GradleProjectResolverExtension.EP_NAME.getPoint().registerExtension(TestPartialProjectResolverExtension(), testRootDisposable) - ProjectModelContributor.EP_NAME.getPoint().registerExtension(TestProjectModelContributor(), testRootDisposable) + GradleProjectResolverExtension.EP_NAME.point.registerExtension(TestPartialProjectResolverExtension(), testRootDisposable) + ProjectModelContributor.EP_NAME.point.registerExtension(TestProjectModelContributor(), testRootDisposable) } @@ -37,8 +42,9 @@ class GradlePartialImportingTest : BuildViewMessagesImportingTestCase() { fun `test re-import with partial project data resolve`() { createAndImportTestProject() assertReceivedModels( - mapOf("prop1" to "val1"), - mapOf("prop2" to "val2") + projectPath, "project", + mapOf("name" to "project", "prop_loaded_1" to "val1"), + mapOf("name" to "project", "prop_finished_2" to "val2") ) val initialProjectStructure = ProjectDataManager.getInstance() @@ -48,8 +54,8 @@ class GradlePartialImportingTest : BuildViewMessagesImportingTestCase() { createProjectSubFile( "gradle.properties", - "prop1=val1_inc\n" + - "prop2=val2_inc\n" + "prop_loaded_1=val1_inc\n" + + "prop_finished_2=val2_inc\n" ) cleanupBeforeReImport() @@ -68,8 +74,9 @@ class GradlePartialImportingTest : BuildViewMessagesImportingTestCase() { ) assertReceivedModels( - mapOf("prop1" to "val1_inc"), - mapOf("prop2" to "val2_inc") + projectPath, "project", + mapOf("name" to "project", "prop_loaded_1" to "val1_inc"), + mapOf("name" to "project", "prop_finished_2" to "val2_inc") ) val projectStructureAfterIncrementalImport = ProjectDataManager.getInstance() @@ -80,18 +87,142 @@ class GradlePartialImportingTest : BuildViewMessagesImportingTestCase() { assertEquals(initialProjectStructure, projectStructureAfterIncrementalImport) } + @Test + @TargetVersions("3.3+") + fun `test composite project partial re-import`() { + createAndImportTestCompositeProject() + + assertReceivedModels( + projectPath, "project", + mapOf("name" to "project", "prop_loaded_1" to "val1"), + mapOf("name" to "project", "prop_finished_2" to "val2") + ) + assertReceivedModels( + path("buildSrc"), "buildSrc", + mapOf("name" to "buildSrc"), + mapOf("name" to "buildSrc") + ) + assertReceivedModels( + path("includedBuild"), "includedBuild", + mapOf("name" to "includedBuild", "prop_loaded_included" to "val1"), + mapOf("name" to "includedBuild", "prop_finished_included" to "val2") + ) + assertReceivedModels( + path("includedBuild"), "subProject", + mapOf("name" to "subProject", "prop_loaded_included" to "val1"), + mapOf("name" to "subProject", "prop_finished_included" to "val2") + ) + assertReceivedModels( + path("includedBuild/buildSrc"), "buildSrc", + mapOf("name" to "buildSrc"), + mapOf("name" to "buildSrc") + ) + + val initialProjectStructure = ProjectDataManager.getInstance() + .getExternalProjectData(myProject, SYSTEM_ID, projectPath)!! + .externalProjectStructure!! + .graphCopy() + + createProjectSubFile( + "gradle.properties", + "prop_loaded_1=val1_inc\n" + + "prop_finished_2=val2_inc\n" + ) + createProjectSubFile( + "includedBuild/gradle.properties", + "prop_loaded_included=val1_1\n" + + "prop_finished_included=val2_2\n" + ) + + cleanupBeforeReImport() + ExternalSystemUtil.refreshProject( + projectPath, + ImportSpecBuilder(myProject, SYSTEM_ID) + .use(ProgressExecutionMode.MODAL_SYNC) + .projectResolverPolicy( + GradlePartialResolverPolicy(Predicate { it is TestPartialProjectResolverExtension }) + ) + ) + + assertReceivedModels( + projectPath, "project", + mapOf("name" to "project", "prop_loaded_1" to "val1_inc"), + mapOf("name" to "project", "prop_finished_2" to "val2_inc") + ) + assertReceivedModels( + path("buildSrc"), "buildSrc", + mapOf("name" to "buildSrc"), + mapOf("name" to "buildSrc") + ) + assertReceivedModels( + path("includedBuild"), "includedBuild", + mapOf("name" to "includedBuild", "prop_loaded_included" to "val1_1"), + mapOf("name" to "includedBuild", "prop_finished_included" to "val2_2") + ) + assertReceivedModels( + path("includedBuild"), "subProject", + mapOf("name" to "subProject", "prop_loaded_included" to "val1_1"), + mapOf("name" to "subProject", "prop_finished_included" to "val2_2") + ) + assertReceivedModels( + path("includedBuild/buildSrc"), "buildSrc", + mapOf("name" to "buildSrc"), + mapOf("name" to "buildSrc") + ) + + val projectStructureAfterIncrementalImport = ProjectDataManager.getInstance() + .getExternalProjectData(myProject, SYSTEM_ID, projectPath)!! + .externalProjectStructure!! + .graphCopy() + + assertEquals(initialProjectStructure, projectStructureAfterIncrementalImport) + } + + private fun createAndImportTestCompositeProject() { + createProjectSubFile( + "buildSrc/build.gradle", + "apply plugin: 'groovy'\n" + + "dependencies {\n" + + " compile gradleApi()\n" + + " compile localGroovy()\n" + + "}" + ) + createProjectSubFile( + "gradle.properties", + "prop_loaded_1=val1\n" + + "prop_finished_2=val2\n" + ) + createProjectSubFile("includedBuild/settings.gradle", "include 'subProject'") + createProjectSubDir("includedBuild/subProject") + createProjectSubFile( + "includedBuild/buildSrc/build.gradle", + "apply plugin: 'groovy'\n" + + "dependencies {\n" + + " compile gradleApi()\n" + + " compile localGroovy()\n" + + "}" + ) + createSettingsFile("includeBuild 'includedBuild'") + createProjectSubFile( + "includedBuild/gradle.properties", + "prop_loaded_included=val1\n" + + "prop_finished_included=val2\n" + ) + importProject("") + } + @Test fun `test import cancellation on project loaded phase`() { createAndImportTestProject() - assertReceivedModels( - mapOf("prop1" to "val1"), - mapOf("prop2" to "val2") + assertReceivedModels(projectPath, "project", + mapOf("name" to "project", "prop_loaded_1" to "val1"), + mapOf("name" to "project", "prop_finished_2" to "val2") ) createProjectSubFile( "gradle.properties", - "prop1=error\n" + - "prop2=val22\n" + "prop_loaded_1=error\n" + + "prop_finished_2=val22\n" ) cleanupBeforeReImport() @@ -105,14 +236,16 @@ class GradlePartialImportingTest : BuildViewMessagesImportingTestCase() { " Build cancelled" ) } - assertReceivedModels(mapOf("prop1" to "error")) + assertReceivedModels(projectPath, "project", mapOf("name" to "project", "prop_loaded_1" to "error")) } else { assertSyncViewTreeEquals( "-\n" + " finished" ) - assertReceivedModels(mapOf("prop1" to "error"), mapOf("prop2" to "val22")) + assertReceivedModels(projectPath, "project", + mapOf("name" to "project", "prop_loaded_1" to "error"), + mapOf("name" to "project", "prop_finished_2" to "val22")) } } @@ -122,76 +255,44 @@ class GradlePartialImportingTest : BuildViewMessagesImportingTestCase() { } private fun createAndImportTestProject() { - val injectModelBuilder = """ - import org.gradle.api.Project; - import javax.inject.Inject; - import org.gradle.tooling.provider.model.ToolingModelBuilder; - import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry; - class TestPlugin implements Plugin { - private ToolingModelBuilderRegistry registry; - - @Inject - TestPlugin(ToolingModelBuilderRegistry registry) { - this.registry = registry; - } - - void apply(Project project) { - registry.register(new MyModelBuilder1()) - registry.register(new MyModelBuilder2()) - } - - private static class MyModelBuilder1 implements ToolingModelBuilder { - boolean canBuild(String modelName) { - return 'java.util.HashMap' == modelName; - } - - @Override - Object buildAll(String modelName, Project project) { - def map = new HashMap<>() - map.put("prop1", project.properties['prop1']) - return map; - } - } - - private static class MyModelBuilder2 implements ToolingModelBuilder { - boolean canBuild(String modelName) { - return 'java.util.LinkedHashMap' == modelName; - } - - @Override - Object buildAll(String modelName, Project project) { - def map = new LinkedHashMap<>() - map.put("prop2", project.properties['prop2']) - return map; - } - } - } - """.trimIndent() createProjectSubFile( "gradle.properties", - "prop1=val1\n" + - "prop2=val2\n" + "prop_loaded_1=val1\n" + + "prop_finished_2=val2\n" ) importProject( GradleBuildScriptBuilderEx() .withJavaPlugin() - .addPostfix(injectModelBuilder) - .applyPlugin("TestPlugin") .generate() ) } private fun assertReceivedModels( + buildPath: String, projectName: String, expectedProjectLoadedModelsMap: Map, expectedBuildFinishedModelsMap: Map? = null ) { val modelConsumer = myProject.getService(ModelConsumer::class.java) - assertSize(1, modelConsumer.projectLoadedModels) - assertMapsEqual(expectedProjectLoadedModelsMap, modelConsumer.projectLoadedModels.first().second.map) + val projectLoadedPredicate = Predicate> { + val project = it.first + project.name == projectName && + pathsEqual(project.projectIdentifier.buildIdentifier.rootDir.path, buildPath) + } + assertThat(modelConsumer.projectLoadedModels) + .haveExactly(1, Condition(projectLoadedPredicate, "project loaded model for '$projectName' at '$buildPath'")) + val (_, projectLoadedModel) = modelConsumer.projectLoadedModels.find(projectLoadedPredicate::test)!! + assertMapsEqual(expectedProjectLoadedModelsMap, projectLoadedModel.map) if (expectedBuildFinishedModelsMap != null) { - assertSize(1, modelConsumer.buildFinishedModels) - assertMapsEqual(expectedBuildFinishedModelsMap, modelConsumer.buildFinishedModels.first().second.map) + val buildFinishedPredicate = Predicate> { + val project = it.first + project.name == projectName && + pathsEqual(project.projectIdentifier.buildIdentifier.rootDir.path, buildPath) + } + assertThat(modelConsumer.buildFinishedModels) + .haveExactly(1, Condition(buildFinishedPredicate, "build finished model for '$projectName' at '$buildPath'")) + val (_, buildFinishedModel) = modelConsumer.buildFinishedModels.find(buildFinishedPredicate::test)!! + assertMapsEqual(expectedBuildFinishedModelsMap, buildFinishedModel.map) } else { assertEmpty(modelConsumer.buildFinishedModels) @@ -201,6 +302,10 @@ class GradlePartialImportingTest : BuildViewMessagesImportingTestCase() { class TestPartialProjectResolverExtension : AbstractProjectResolverExtension() { + override fun getToolingExtensionsClasses(): Set> { + return setOf(ProjectProperties::class.java) + } + override fun projectsLoaded(models: ModelsHolder?) { val buildFinishedModel = models?.getModel(BuildFinishedModel::class.java) if (buildFinishedModel != null) { @@ -229,8 +334,9 @@ class TestPartialProjectResolverExtension : AbstractProjectResolverExtension() { projectModel: Model, modelConsumer: ProjectImportModelProvider.ProjectModelConsumer ) { - val model = controller.getModel(projectModel, HashMap::class.java) - modelConsumer.consume(ProjectLoadedModel(model), ProjectLoadedModel::class.java) + val model = controller.getModel(projectModel, ProjectProperties::class.java) + modelConsumer.consume(ProjectLoadedModel(model.propertiesMap.filterKeys { it == "name" || it.startsWith("prop_loaded_") }), + ProjectLoadedModel::class.java) } override fun populateBuildModels( @@ -249,8 +355,9 @@ class TestPartialProjectResolverExtension : AbstractProjectResolverExtension() { projectModel: Model, modelConsumer: ProjectImportModelProvider.ProjectModelConsumer ) { - val model = controller.getModel(projectModel, LinkedHashMap::class.java) - modelConsumer.consume(BuildFinishedModel(model), BuildFinishedModel::class.java) + val model = controller.getModel(projectModel, ProjectProperties::class.java) + modelConsumer.consume(BuildFinishedModel(model.propertiesMap.filterKeys { it == "name" || it.startsWith("prop_finished_") }), + BuildFinishedModel::class.java) } override fun populateBuildModels( @@ -263,7 +370,7 @@ class TestPartialProjectResolverExtension : AbstractProjectResolverExtension() { } } -internal class TestProjectModelContributor() : ProjectModelContributor { +internal class TestProjectModelContributor : ProjectModelContributor { override fun accept( modifiableGradleProjectModel: ModifiableGradleProjectModel, toolingModelsProvider: ToolingModelsProvider, diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleBuildSrcProjectsResolver.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleBuildSrcProjectsResolver.java index 48edfcb0bd13..a856acc51462 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleBuildSrcProjectsResolver.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleBuildSrcProjectsResolver.java @@ -15,6 +15,7 @@ import org.gradle.tooling.ProjectConnection; import org.gradle.tooling.model.build.BuildEnvironment; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.gradle.model.Build; import org.jetbrains.plugins.gradle.model.data.BuildParticipant; import org.jetbrains.plugins.gradle.model.data.BuildScriptClasspathData; import org.jetbrains.plugins.gradle.model.data.CompositeBuildData; @@ -25,6 +26,7 @@ import org.jetbrains.plugins.gradle.util.GradleConstants; import java.io.File; import java.util.*; +import java.util.stream.Stream; import static com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil.*; import static com.intellij.openapi.util.text.StringUtil.isEmpty; @@ -110,8 +112,10 @@ public final class GradleBuildSrcProjectsResolver { jvmOptions.addAll(myMainBuildExecutionSettings.getJvmArguments()); } - for (String buildPath : buildClasspathNodesMap.keySet()) { - Collection> buildClasspathNodes = buildClasspathNodesMap.get(buildPath); + Stream builds = new ToolingModelsProviderImpl(myResolverContext.getModels()).builds(); + builds.forEach(build -> { + String buildPath = build.getBuildIdentifier().getRootDir().getPath(); + Collection> buildClasspathNodes = buildClasspathNodesMap.getModifiable(buildPath); GradleExecutionSettings buildSrcProjectSettings; if (gradleHome != null) { @@ -155,7 +159,7 @@ public final class GradleBuildSrcProjectsResolver { includedModulesPaths, buildSrcResolverCtx, myProjectResolver.getProjectDataFunction(buildSrcResolverCtx, myResolverChain, true)); - } + }); } private void handleBuildSrcProject(@NotNull DataNode resultProjectDataNode, diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolver.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolver.java index 260c42ae1bb4..af57ebf1adb0 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolver.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/GradleProjectResolver.java @@ -477,21 +477,24 @@ public class GradleProjectResolver implements ExternalSystemProjectResolver getPropertiesMap(); + } + + public static class ProjectPropertiesImpl implements ProjectProperties, Serializable { + private final Map myPropertiesMap; + + public ProjectPropertiesImpl(Map propertiesMap) { + myPropertiesMap = propertiesMap; + } + + @Override + public Map getPropertiesMap() { + return myPropertiesMap; + } + } +}