From 8d7d8a03842dd0b266ac42073d0d5356d0a64e40 Mon Sep 17 00:00:00 2001 From: nik Date: Mon, 25 Jan 2016 16:45:38 +0300 Subject: [PATCH] IDEA-150835: Provide an API which tells which production module corresponding to tests module --- .../roots/ModuleTestPropertiesTest.java | 32 ++++++++ .../model/project/ModuleData.java | 14 ++++ .../project/IdeModifiableModelsProvider.java | 2 + .../AbstractIdeModifiableModelsProvider.java | 9 +++ .../manage/AbstractModuleDataService.java | 4 + .../src/META-INF/LangExtensions.xml | 2 + .../openapi/roots/TestModuleProperties.java | 45 +++++++++++ .../roots/impl/TestModulePropertiesImpl.java | 79 +++++++++++++++++++ .../BaseGradleProjectResolverExtension.java | 19 +++-- 9 files changed, 201 insertions(+), 5 deletions(-) create mode 100644 java/java-tests/testSrc/com/intellij/roots/ModuleTestPropertiesTest.java create mode 100644 platform/projectModel-impl/src/com/intellij/openapi/roots/TestModuleProperties.java create mode 100644 platform/projectModel-impl/src/com/intellij/openapi/roots/impl/TestModulePropertiesImpl.java diff --git a/java/java-tests/testSrc/com/intellij/roots/ModuleTestPropertiesTest.java b/java/java-tests/testSrc/com/intellij/roots/ModuleTestPropertiesTest.java new file mode 100644 index 000000000000..3abc565c5b9d --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/roots/ModuleTestPropertiesTest.java @@ -0,0 +1,32 @@ +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.roots; + +import com.intellij.openapi.module.Module; +import com.intellij.openapi.roots.TestModuleProperties; +import com.intellij.testFramework.ModuleTestCase; + +/** + * @author nik + */ +public class ModuleTestPropertiesTest extends ModuleTestCase { + public void testSetAndGet() throws Exception { + Module tests = createModule("tests"); + TestModuleProperties moduleProperties = TestModuleProperties.getInstance(tests); + moduleProperties.setProductionModuleName(myModule.getName()); + assertSame(myModule, moduleProperties.getProductionModule()); + } +} diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/project/ModuleData.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/project/ModuleData.java index 0dd514a23718..4179681ab59b 100644 --- a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/project/ModuleData.java +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/project/ModuleData.java @@ -33,6 +33,7 @@ public class ModuleData extends AbstractNamedData implements Named, ExternalConf @Nullable private String[] myIdeModuleGroup; @Nullable private String mySourceCompatibility; @Nullable private String myTargetCompatibility; + @Nullable private String myProductionModuleId; private boolean myInheritProjectCompileOutputPath = true; @@ -97,6 +98,19 @@ public class ModuleData extends AbstractNamedData implements Named, ExternalConf myModuleFileDirectoryPath = path; } + /** + * @return an internal id of production module corresponding to a test-only module, this information is used to populate + * {@link com.intellij.openapi.roots.TestModuleProperties} + */ + @Nullable + public String getProductionModuleId() { + return myProductionModuleId; + } + + public void setProductionModuleId(@Nullable String productionModuleId) { + myProductionModuleId = productionModuleId; + } + public boolean isInheritProjectCompileOutputPath() { return myInheritProjectCompileOutputPath; } diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/service/project/IdeModifiableModelsProvider.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/service/project/IdeModifiableModelsProvider.java index 33148bcea989..a72dc7cc601c 100644 --- a/platform/external-system-api/src/com/intellij/openapi/externalSystem/service/project/IdeModifiableModelsProvider.java +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/service/project/IdeModifiableModelsProvider.java @@ -65,4 +65,6 @@ public interface IdeModifiableModelsProvider extends IdeModelsProvider { void commit(); void dispose(); + + void setTestModuleProperties(Module testModule, String productionModuleName); } diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/AbstractIdeModifiableModelsProvider.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/AbstractIdeModifiableModelsProvider.java index 1721400de607..cc2de2f9af28 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/AbstractIdeModifiableModelsProvider.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/AbstractIdeModifiableModelsProvider.java @@ -55,6 +55,7 @@ public abstract class AbstractIdeModifiableModelsProvider extends IdeModelsProvi private ModifiableModuleModel myModifiableModuleModel; private Map myModifiableRootModels = new THashMap(); private Map myModifiableFacetModels = new THashMap(); + private Map myProductionModulesForTestModules = new THashMap(); private Map myModifiableLibraryModels = new IdentityHashMap(); private ModifiableArtifactModel myModifiableArtifactModel; private AbstractIdeModifiableModelsProvider.MyPackagingElementResolvingContext myPackagingElementResolvingContext; @@ -361,6 +362,9 @@ public abstract class AbstractIdeModifiableModelsProvider extends IdeModelsProvi model.commit(); } } + for (Map.Entry entry : myProductionModulesForTestModules.entrySet()) { + TestModuleProperties.getInstance(entry.getKey()).setProductionModuleName(entry.getValue()); + } for (Map.Entry each : myModifiableFacetModels.entrySet()) { if(!each.getKey().isDisposed()) { @@ -397,4 +401,9 @@ public abstract class AbstractIdeModifiableModelsProvider extends IdeModelsProvi myModifiableFacetModels.clear(); myModifiableLibraryModels.clear(); } + + @Override + public void setTestModuleProperties(Module testModule, String productionModuleName) { + myProductionModulesForTestModules.put(testModule, productionModuleName); + } } diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/AbstractModuleDataService.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/AbstractModuleDataService.java index 1a60ecb643e2..60c9d7d4e7b6 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/AbstractModuleDataService.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/AbstractModuleDataService.java @@ -123,6 +123,10 @@ public abstract class AbstractModuleDataService extends Ab ModuleData data = module.getData(); final Module created = modelsProvider.newModule(data.getModuleFilePath(), data.getModuleTypeId()); module.putUserData(MODULE_KEY, created); + String productionModuleId = data.getProductionModuleId(); + if (productionModuleId != null) { + modelsProvider.setTestModuleProperties(created, productionModuleId); + } Set orphanFiles = project.getUserData(ORPHAN_MODULE_FILES); if (orphanFiles != null) { orphanFiles.remove(created.getModuleFilePath()); diff --git a/platform/platform-resources/src/META-INF/LangExtensions.xml b/platform/platform-resources/src/META-INF/LangExtensions.xml index bfc607c14c5b..9ba616080ea8 100644 --- a/platform/platform-resources/src/META-INF/LangExtensions.xml +++ b/platform/platform-resources/src/META-INF/LangExtensions.xml @@ -341,6 +341,8 @@ + diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/TestModuleProperties.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/TestModuleProperties.java new file mode 100644 index 000000000000..750ecbe44655 --- /dev/null +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/TestModuleProperties.java @@ -0,0 +1,45 @@ +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.openapi.roots; + +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleServiceManager; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * In some cases tests need to be extracted to a separate module (because they have a different classpath, output folder or JDK). E.g. when + * the project is imported from Gradle IDEA creates separate modules for each source set of a Gradle project. + *

+ * This service allows to specify to which production module the tests module belongs. This information may be used for example by + * 'Create Test' feature. + *

+ * This API isn't stable for now and may be changed in future. Also it isn't possible to change this in UI. + * @author nik + */ +public abstract class TestModuleProperties { + public static TestModuleProperties getInstance(@NotNull Module module) { + return ModuleServiceManager.getService(module, TestModuleProperties.class); + } + + @Nullable + public abstract String getProductionModuleName(); + + @Nullable + public abstract Module getProductionModule(); + + public abstract void setProductionModuleName(@Nullable String moduleName); +} diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/TestModulePropertiesImpl.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/TestModulePropertiesImpl.java new file mode 100644 index 000000000000..221806b98ced --- /dev/null +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/TestModulePropertiesImpl.java @@ -0,0 +1,79 @@ +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.openapi.roots.impl; + +import com.intellij.openapi.components.PersistentStateComponent; +import com.intellij.openapi.components.State; +import com.intellij.openapi.components.Storage; +import com.intellij.openapi.components.StoragePathMacros; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModulePointer; +import com.intellij.openapi.module.ModulePointerManager; +import com.intellij.openapi.roots.TestModuleProperties; +import com.intellij.util.xmlb.annotations.Attribute; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author nik + */ +@State( + name = "TestModuleProperties", + storages = @Storage(file = StoragePathMacros.MODULE_FILE) +) +public class TestModulePropertiesImpl extends TestModuleProperties implements PersistentStateComponent { + private final ModulePointerManager myModulePointerManager; + private ModulePointer myProductionModulePointer; + + public TestModulePropertiesImpl(@NotNull ModulePointerManager modulePointerManager) { + myModulePointerManager = modulePointerManager; + } + + @Nullable + @Override + public String getProductionModuleName() { + return myProductionModulePointer != null ? myProductionModulePointer.getModuleName() : null; + } + + @Nullable + @Override + public Module getProductionModule() { + return myProductionModulePointer != null ? myProductionModulePointer.getModule() : null; + } + + @Override + public void setProductionModuleName(@Nullable String moduleName) { + myProductionModulePointer = moduleName != null ? myModulePointerManager.create(moduleName) : null; + } + + @Nullable + @Override + public TestModulePropertiesState getState() { + TestModulePropertiesState state = new TestModulePropertiesState(); + state.moduleName = getProductionModuleName(); + return state; + } + + @Override + public void loadState(TestModulePropertiesState state) { + + } + + public static class TestModulePropertiesState { + @Attribute("production-module") + public String moduleName; + } +} diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/BaseGradleProjectResolverExtension.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/BaseGradleProjectResolverExtension.java index 09ce935ed101..2a7f6b2eb6c2 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/BaseGradleProjectResolverExtension.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/BaseGradleProjectResolverExtension.java @@ -178,7 +178,7 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver for (ExternalSourceSet sourceSet : externalProject.getSourceSets().values()) { final String moduleId = getModuleId(externalProject, sourceSet); final String moduleExternalName = gradleModule.getName() + ":" + sourceSet.getName(); - final String moduleInternalName = gradleModule.getName() + "_" + sourceSet.getName(); + final String moduleInternalName = getInternalModuleName(gradleModule, sourceSet.getName()); GradleSourceSetData sourceSetData = new GradleSourceSetData( moduleId, moduleExternalName, moduleInternalName, mainModuleFileDirectoryPath, mainModuleConfigPath); @@ -204,10 +204,14 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver } artifacts.addAll(archivesArtifacts); } - } else if("test".equals(sourceSet.getName())) { - final Set testsArtifacts = externalProject.getArtifactsByConfiguration().get("tests"); - if (testsArtifacts != null) { - artifacts.addAll(testsArtifacts); + } + else { + sourceSetData.setProductionModuleId(getInternalModuleName(gradleModule, "main")); + if ("test".equals(sourceSet.getName())) { + final Set testsArtifacts = externalProject.getArtifactsByConfiguration().get("tests"); + if (testsArtifacts != null) { + artifacts.addAll(testsArtifacts); + } } } sourceSetData.setArtifacts(ContainerUtil.newArrayList(artifacts)); @@ -229,6 +233,11 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver return mainModuleNode; } + @NotNull + public String getInternalModuleName(@NotNull IdeaModule gradleModule, @NotNull String sourceSetName) { + return gradleModule.getName() + "_" + sourceSetName; + } + @Override public void populateModuleExtraModels(@NotNull IdeaModule gradleModule, @NotNull DataNode ideModule) { final BuildScriptClasspathModel buildScriptClasspathModel = resolverCtx.getExtraProject(gradleModule, BuildScriptClasspathModel.class); -- 2.23.3