[project model tests] rewrite test on loading/unloading custom source root types

The old test ModuleRootsExternalizationTest::testChangeRootType called method which change root types directly, and making this test to work in the new project model won't be easy, because it stores real data in internal entities and produce implementation of the project model interfaces lazily. The new CustomSourceRootTypeTest works on higher level, it checks loading and unloading source root types by registering and unregistering corresponding extension.

GitOrigin-RevId: 2071d30372aac1a9d1c83955afa1a6e2701f7a1b
This commit is contained in:
Nikolay Chashnikov
2020-05-26 12:32:54 +03:00
committed by intellij-monorepo-bot
parent 5b27a05f5f
commit 82ee0cc693
10 changed files with 149 additions and 92 deletions
@@ -4,7 +4,6 @@ package com.intellij.roots;
import com.intellij.application.options.ReplacePathToMacroMap;
import com.intellij.configurationStore.StoreUtil;
import com.intellij.ide.highlighter.ModuleFileType;
import com.intellij.jps.impl.JpsIdePluginManagerImpl;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.ExpandMacroToPathMap;
import com.intellij.openapi.module.Module;
@@ -21,19 +20,16 @@ import com.intellij.testFramework.JavaModuleTestCase;
import com.intellij.testFramework.PsiTestUtil;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jetbrains.jps.model.JpsElement;
import org.jetbrains.jps.model.java.JavaSourceRootProperties;
import org.jetbrains.jps.model.java.JavaSourceRootType;
import org.jetbrains.jps.model.java.JpsJavaExtensionService;
import org.jetbrains.jps.model.module.JpsModuleSourceRootType;
import org.jetbrains.jps.model.module.UnknownSourceRootType;
import org.jetbrains.jps.model.module.UnknownSourceRootTypeProperties;
import org.jetbrains.jps.model.serialization.JpsModelSerializerExtension;
import org.jetbrains.jps.model.serialization.module.JpsModuleSourceRootPropertiesSerializer;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.stream.Collectors;
import static com.intellij.testFramework.assertions.Assertions.assertThat;
@@ -151,70 +147,6 @@ public class ModuleRootsExternalizationTest extends JavaModuleTestCase {
);
}
public void testChangeRootType() throws JDOMException, IOException {
File content = new File(getProject().getBasePath());
File source = new File(content, "source");
File testSource = new File(content, "testSource");
FileUtil.createDirectory(source);
FileUtil.createDirectory(testSource);
final VirtualFile contentFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(content);
assertNotNull(contentFile);
refreshRecursively(contentFile);
final VirtualFile sourceFile = LocalFileSystem.getInstance().findFileByIoFile(source);
assertNotNull(sourceFile);
final VirtualFile testSourceFile = LocalFileSystem.getInstance().findFileByIoFile(testSource);
assertNotNull(testSourceFile);
final File moduleFile = new File(content, "test.iml");
final Module module = createModule(moduleFile);
PsiTestUtil.addContentRoot(module, contentFile);
PsiTestUtil.addSourceRoot(
module, sourceFile, JavaSourceRootType.SOURCE, JpsJavaExtensionService.getInstance().createSourceRootProperties("org.jetbrains", true)
);
PsiTestUtil.addSourceRoot(
module, testSourceFile, JavaSourceRootType.TEST_SOURCE, JpsJavaExtensionService.getInstance().createSourceRootProperties("org.jetbrains", false)
);
StoreUtil.saveDocumentsAndProjectSettings(myProject);
String expectedXml = "<component name=\"NewModuleRootManager\" inherit-compiler-output=\"true\">\n" +
" <exclude-output />\n" +
" <content url=\"file://$MODULE_DIR$\">\n" +
" <sourceFolder url=\"file://$MODULE_DIR$/source\" isTestSource=\"false\" packagePrefix=\"org.jetbrains\" generated=\"true\" />\n" +
" <sourceFolder url=\"file://$MODULE_DIR$/testSource\" isTestSource=\"true\" packagePrefix=\"org.jetbrains\" />\n" +
" </content>\n" +
" <orderEntry type=\"sourceFolder\" forTests=\"false\" />\n" +
"</component>";
assertEquals(expectedXml, JDOMUtil.writeElement(JDOMUtil.load(moduleFile).getChild("component")));
JpsIdePluginManagerImpl.replaceWithUnknownRootType(myProject, findSerializers(Arrays.asList(JavaSourceRootType.SOURCE, JavaSourceRootType.TEST_SOURCE)));
for (ContentEntry entry : ModuleRootManager.getInstance(module).getContentEntries()) {
for (SourceFolder folder : entry.getSourceFolders()) {
assertTrue("Root type expected to be 'Unknown' for " + folder.getUrl(), folder.getRootType() instanceof UnknownSourceRootType);
JpsElement properties = folder.getJpsElement().getProperties(folder.getRootType());
assertTrue(properties instanceof UnknownSourceRootTypeProperties<?>);
assertTrue(((UnknownSourceRootTypeProperties<?>)properties).getPropertiesData() instanceof Element);
}
}
StoreUtil.saveSettings(myProject, true);
assertEquals(expectedXml, JDOMUtil.writeElement(JDOMUtil.load(moduleFile).getChild("component")));
JpsIdePluginManagerImpl.updateCustomRootTypes(myProject, findSerializers(Arrays.asList(JavaSourceRootType.SOURCE, JavaSourceRootType.TEST_SOURCE)));
for (ContentEntry entry : ModuleRootManager.getInstance(module).getContentEntries()) {
for (SourceFolder folder : entry.getSourceFolders()) {
assertFalse("'Unknown' root type is not expected: " + folder.getUrl(), folder.getRootType() instanceof UnknownSourceRootType);
JpsElement properties = folder.getJpsElement().getProperties(folder.getRootType());
assertTrue(properties instanceof JavaSourceRootProperties);
assertEquals("org.jetbrains", ((JavaSourceRootProperties)properties).getPackagePrefix());
}
}
StoreUtil.saveSettings(myProject, true);
assertEquals(expectedXml, JDOMUtil.writeElement(JDOMUtil.load(moduleFile).getChild("component")));
}
private static Collection<JpsModuleSourceRootPropertiesSerializer<?>> findSerializers(Collection<JpsModuleSourceRootType<?>> rootTypes) {
final Set<JpsModuleSourceRootType<?>> typesSet = rootTypes instanceof Set ? (Set<JpsModuleSourceRootType<?>>)rootTypes : new HashSet<>(rootTypes);
Set<JpsModuleSourceRootPropertiesSerializer<?>> result = new HashSet<>();
@@ -35,6 +35,9 @@ public interface ExtensionPoint<@NotNull T> {
@TestOnly
void registerExtension(@NotNull T extension, @NotNull Disposable parentDisposable);
@TestOnly
void registerExtension(@NotNull T extension, @NotNull PluginDescriptor pluginDescriptor, @NotNull Disposable parentDisposable);
/**
* Use {@link com.intellij.testFramework.PlatformTestUtil#maskExtensions}
* to register extension as first or to completely replace existing extensions in tests.
@@ -118,12 +118,18 @@ public abstract class ExtensionPointImpl<@NotNull T> implements ExtensionPoint<T
@Override
public final void registerExtension(@NotNull T extension, @NotNull LoadingOrder order) {
doRegisterExtension(extension, order, null);
doRegisterExtension(extension, order, getPluginDescriptor(), null);
}
@Override
public final void registerExtension(@NotNull T extension, @NotNull Disposable parentDisposable) {
doRegisterExtension(extension, LoadingOrder.ANY, parentDisposable);
registerExtension(extension, getPluginDescriptor(), parentDisposable);
}
@Override
public final void registerExtension(@NotNull T extension,
@NotNull PluginDescriptor pluginDescriptor, @NotNull Disposable parentDisposable) {
doRegisterExtension(extension, LoadingOrder.ANY, pluginDescriptor, parentDisposable);
}
@Override
@@ -133,14 +139,15 @@ public abstract class ExtensionPointImpl<@NotNull T> implements ExtensionPoint<T
@Override
public final void registerExtension(@NotNull T extension, @NotNull LoadingOrder order, @NotNull Disposable parentDisposable) {
doRegisterExtension(extension, order, parentDisposable);
doRegisterExtension(extension, order, getPluginDescriptor(), parentDisposable);
}
public final ComponentManager getComponentManager() {
return componentManager;
}
private synchronized void doRegisterExtension(@NotNull T extension, @NotNull LoadingOrder order, @Nullable Disposable parentDisposable) {
private synchronized void doRegisterExtension(@NotNull T extension, @NotNull LoadingOrder order,
@NotNull PluginDescriptor pluginDescriptor, @Nullable Disposable parentDisposable) {
assertNotReadOnlyMode();
checkExtensionType(extension, getExtensionClass(), null);
@@ -151,7 +158,7 @@ public abstract class ExtensionPointImpl<@NotNull T> implements ExtensionPoint<T
}
}
ObjectComponentAdapter<T> adapter = new ObjectComponentAdapter<>(extension, getPluginDescriptor(), order);
ObjectComponentAdapter<T> adapter = new ObjectComponentAdapter<>(extension, pluginDescriptor, order);
addExtensionAdapter(adapter);
notifyListeners(false, Collections.singletonList(adapter), myListeners);
@@ -0,0 +1,80 @@
// 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 com.intellij.openapi.roots
import com.intellij.openapi.module.Module
import com.intellij.openapi.util.Disposer
import com.intellij.testFramework.ApplicationRule
import com.intellij.testFramework.DisposableRule
import com.intellij.testFramework.rules.ProjectModelRule
import com.intellij.testFramework.rules.TempDirectory
import org.assertj.core.api.Assertions.assertThat
import org.jetbrains.jps.model.module.UnknownSourceRootType
import org.jetbrains.jps.model.serialization.JpsModelSerializerExtension
import org.junit.Before
import org.junit.ClassRule
import org.junit.Rule
import org.junit.Test
class CustomSourceRootTypeTest {
companion object {
@JvmField
@ClassRule
val appRule = ApplicationRule()
}
@Rule
@JvmField
val projectModel = ProjectModelRule()
@Rule
@JvmField
val tempDirectory = TempDirectory()
@Rule
@JvmField
val disposable = DisposableRule()
lateinit var module: Module
@Before
fun setUp() {
module = projectModel.createModule()
JpsModelSerializerExtension.getExtensions()
}
@Test
fun `load unload custom source root`() {
val srcDir = projectModel.baseProjectDir.newVirtualDirectory("src")
runWithRegisteredExtension {
val model = createModifiableModel(module)
model.addContentEntry(srcDir).addSourceFolder(srcDir, TestCustomSourceRootType.INSTANCE, TestCustomSourceRootProperties("hello"))
val committed = commitModifiableRootModel(model)
val sourceFolder = committed.contentEntries.single().sourceFolders.single()
assertThat(sourceFolder.file).isEqualTo(srcDir)
assertThat(sourceFolder.rootType).isEqualTo(TestCustomSourceRootType.INSTANCE)
assertThat((sourceFolder.jpsElement.properties as TestCustomSourceRootProperties).testString).isEqualTo("hello")
}
val sourceFolderWithUnknownType = ModuleRootManager.getInstance(module).contentEntries.single().sourceFolders.single()
assertThat(sourceFolderWithUnknownType.file).isEqualTo(srcDir)
assertThat(sourceFolderWithUnknownType.rootType).isInstanceOf(UnknownSourceRootType::class.java)
TestCustomRootModelSerializerExtension.registerTestCustomSourceRootType(tempDirectory.newDirectory(), disposable.disposable)
val sourceFolder = ModuleRootManager.getInstance(module).contentEntries.single().sourceFolders.single()
assertThat(sourceFolder.file).isEqualTo(srcDir)
assertThat(sourceFolder.rootType).isEqualTo(TestCustomSourceRootType.INSTANCE)
assertThat((sourceFolder.jpsElement.properties as TestCustomSourceRootProperties).testString).isEqualTo("hello")
}
private fun runWithRegisteredExtension(action: () -> Unit) {
val disposable = Disposer.newDisposable()
TestCustomRootModelSerializerExtension.registerTestCustomSourceRootType(tempDirectory.newDirectory(), disposable)
try {
action()
}
finally {
Disposer.dispose(disposable)
}
}
}
@@ -1,17 +1,45 @@
// 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 com.intellij.workspace.jps
// 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.
@file:Suppress("unused")
import com.intellij.openapi.util.JDOMUtil
package com.intellij.openapi.roots
import com.intellij.jps.impl.JpsPluginBean
import com.intellij.openapi.Disposable
import com.intellij.openapi.application.runWriteActionAndWait
import com.intellij.openapi.extensions.DefaultPluginDescriptor
import com.intellij.openapi.extensions.PluginId
import com.intellij.openapi.util.Disposer
import com.intellij.openapi.util.io.FileUtil
import com.intellij.util.lang.UrlClassLoader
import org.jdom.Element
import org.jetbrains.jps.model.ex.JpsElementBase
import org.jetbrains.jps.model.ex.JpsElementTypeBase
import org.jetbrains.jps.model.module.JpsModuleSourceRootType
import org.jetbrains.jps.model.serialization.JpsModelSerializerExtension
import org.jetbrains.jps.model.serialization.module.JpsModuleSourceRootPropertiesSerializer
import java.io.File
class TestCustomRootModelSerializerExtension : JpsModelSerializerExtension() {
override fun getModuleSourceRootPropertiesSerializers(): List<JpsModuleSourceRootPropertiesSerializer<*>> =
listOf(TestCustomSourceRootPropertiesSerializer(TestCustomSourceRootType.INSTANCE, TestCustomSourceRootType.TYPE_ID))
companion object {
fun registerTestCustomSourceRootType(tempPluginRoot: File, disposable: Disposable) {
val jpsPluginDisposable = Disposer.newDisposable()
Disposer.register(disposable, Disposable {
runWriteActionAndWait {
Disposer.dispose(jpsPluginDisposable)
}
})
FileUtil.writeToFile(File(tempPluginRoot, "META-INF/services/${JpsModelSerializerExtension::class.java.name}"),
TestCustomRootModelSerializerExtension::class.java.name)
val pluginClassLoader = UrlClassLoader.build().parent(TestCustomRootModelSerializerExtension::class.java.classLoader).urls(tempPluginRoot.toURI().toURL()).get()
val pluginDescriptor = DefaultPluginDescriptor(PluginId.getId("com.intellij.custom.source.root.test"), pluginClassLoader)
JpsPluginBean.EP_NAME.point.registerExtension(JpsPluginBean(), pluginDescriptor, jpsPluginDisposable)
}
}
}
class TestCustomSourceRootType private constructor() : JpsElementTypeBase<TestCustomSourceRootProperties>(), JpsModuleSourceRootType<TestCustomSourceRootProperties> {
@@ -48,9 +76,6 @@ class TestCustomSourceRootPropertiesSerializer(
: JpsModuleSourceRootPropertiesSerializer<TestCustomSourceRootProperties>(type, typeId) {
override fun loadProperties(sourceRootTag: Element): TestCustomSourceRootProperties {
if (sourceRootTag.getAttributeValue("url") == null) error("url is missing in '${JDOMUtil.writeElement(sourceRootTag)}'")
if (sourceRootTag.getAttributeValue("type") != typeId) error("expected type '$typeId' in '${JDOMUtil.writeElement(sourceRootTag)}'")
val testString = sourceRootTag.getAttributeValue("testString")
return TestCustomSourceRootProperties(testString)
}
@@ -145,7 +145,7 @@ public final class JpsIdePluginManagerImpl extends JpsPluginManager {
}
}
public static void replaceWithUnknownRootType(Project project, Collection<JpsModuleSourceRootPropertiesSerializer<?>> unregisteredSerializers) {
private static void replaceWithUnknownRootType(Project project, Collection<JpsModuleSourceRootPropertiesSerializer<?>> unregisteredSerializers) {
if (unregisteredSerializers.isEmpty()) {
return;
}
@@ -174,7 +174,7 @@ public final class JpsIdePluginManagerImpl extends JpsPluginManager {
}
}
public static void updateCustomRootTypes(Project project, Collection<JpsModuleSourceRootPropertiesSerializer<?>> registeredSerializers) {
private static void updateCustomRootTypes(Project project, Collection<JpsModuleSourceRootPropertiesSerializer<?>> registeredSerializers) {
if (registeredSerializers.isEmpty()) {
return;
}
@@ -1,6 +1,7 @@
// 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 com.intellij.testFramework.rules
import com.intellij.openapi.application.invokeAndWaitIfNeeded
import com.intellij.openapi.application.runReadAction
import com.intellij.openapi.application.runWriteActionAndWait
import com.intellij.openapi.module.EmptyModuleType
@@ -30,7 +31,12 @@ import java.io.File
class ProjectModelRule : TestRule {
val baseProjectDir = TempDirectory()
private val projectDelegate = lazy { createHeavyProject(baseProjectDir.root.toPath()) }
private val projectDelegate = lazy { val project = createHeavyProject(baseProjectDir.root.toPath())
invokeAndWaitIfNeeded {
ProjectManagerEx.getInstanceEx().openTestProject(project)
}
project
}
private val disposableRule = DisposableRule()
val project by projectDelegate
private val closeProject = object : ExternalResource() {
@@ -50,8 +56,9 @@ class ProjectModelRule : TestRule {
fun createModule(name: String = "module"): Module {
val imlFile = File(baseProjectDir.root, "$name/$name.iml")
val manager = moduleManager
return runWriteActionAndWait {
moduleManager.newModule(imlFile.systemIndependentPath, EmptyModuleType.EMPTY_MODULE)
manager.newModule(imlFile.systemIndependentPath, EmptyModuleType.EMPTY_MODULE)
}
}
@@ -13,6 +13,7 @@
<orderEntry type="module" module-name="intellij.platform.smRunner" scope="TEST" />
<orderEntry type="module" module-name="intellij.platform.testFramework" scope="TEST" />
<orderEntry type="library" name="fastutil-min" level="project" />
<orderEntry type="module" module-name="intellij.platform.lang.tests" />
</component>
<component name="TestModuleProperties" production-module="intellij.platform.workspaceModel.ide" />
</module>
@@ -1 +0,0 @@
com.intellij.workspace.jps.TestCustomRootModelSerializerExtension
@@ -456,6 +456,8 @@ class LegacyBridgeModulesTest {
@Test
fun `test custom source root loading`() {
TestCustomRootModelSerializerExtension.registerTestCustomSourceRootType(temporaryDirectoryRule.newPath().toFile(),
disposableRule.disposable)
val tempDir = temporaryDirectoryRule.newPath().toFile()
val moduleImlFile = File(tempDir, "my.iml")
Files.createDirectories(moduleImlFile.parentFile.toPath())
@@ -534,6 +536,7 @@ class LegacyBridgeModulesTest {
@Test
fun `test custom source root saving`() {
val tempDir = temporaryDirectoryRule.newPath().toFile()
TestCustomRootModelSerializerExtension.registerTestCustomSourceRootType(temporaryDirectoryRule.newPath().toFile(), disposableRule.disposable)
val moduleImlFile = File(tempDir, "my.iml")
Files.createDirectories(moduleImlFile.parentFile.toPath())
@@ -581,17 +584,17 @@ class LegacyBridgeModulesTest {
val tempDir = temporaryDirectoryRule.newPath().toFile()
val url = VfsUtilCore.pathToUrl(FileUtil.toSystemIndependentName(tempDir.path))
val contentEntry = model.addContentEntry(url)
contentEntry.addSourceFolder("$url/$antLibraryFolder", TestCustomSourceRootType.INSTANCE)
contentEntry.addSourceFolder("$url/$antLibraryFolder", false)
}
StoreUtil.saveDocumentsAndProjectSettings(project)
assertTrue(moduleFile.readText().contains(antLibraryFolder))
val entityStore = WorkspaceModel.getInstance(project).entityStore
assertEquals(1, entityStore.current.entities(ContentRootEntity::class.java).count())
assertEquals(1, entityStore.current.entities(CustomSourceRootPropertiesEntity::class.java).count())
assertEquals(1, entityStore.current.entities(JavaSourceRootEntity::class.java).count())
ModuleManager.getInstance(project).disposeModule(module)
assertEmpty(entityStore.current.entities(ContentRootEntity::class.java).toList())
assertEmpty(entityStore.current.entities(CustomSourceRootPropertiesEntity::class.java).toList())
assertEmpty(entityStore.current.entities(JavaSourceRootEntity::class.java).toList())
}
@Test
@@ -610,7 +613,7 @@ class LegacyBridgeModulesTest {
val tempDir = temporaryDirectoryRule.newPath().toFile()
val url = VfsUtilCore.pathToUrl(FileUtil.toSystemIndependentName(tempDir.path))
val contentEntry = model.addContentEntry(url)
contentEntry.addSourceFolder("$url/$antLibraryFolder", TestCustomSourceRootType.INSTANCE)
contentEntry.addSourceFolder("$url/$antLibraryFolder", false)
}
val entityStore = WorkspaceModel.getInstance(project).entityStore