/* * 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.testFramework; import com.intellij.openapi.application.*; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.module.ModifiableModuleModel; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.module.ModuleType; import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.projectRoots.SdkModificator; import com.intellij.openapi.roots.*; import com.intellij.openapi.roots.impl.ContentEntryImpl; import com.intellij.openapi.roots.impl.libraries.ProjectLibraryTable; import com.intellij.openapi.roots.libraries.Library; import com.intellij.openapi.roots.libraries.LibraryTable; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.*; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiFileFactory; import com.intellij.psi.impl.DebugUtil; import com.intellij.util.IncorrectOperationException; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.model.JpsElement; import org.jetbrains.jps.model.java.JavaSourceRootType; import org.jetbrains.jps.model.module.JpsModuleSourceRootType; import org.junit.Assert; import java.io.File; import java.io.IOException; import java.util.*; public class PsiTestUtil { public static VirtualFile createTestProjectStructure(Project project, Module module, String rootPath, Collection<File> filesToDelete) throws Exception { return createTestProjectStructure(project, module, rootPath, filesToDelete, true); } public static VirtualFile createTestProjectStructure(Project project, Module module, Collection<File> filesToDelete) throws IOException { return createTestProjectStructure(project, module, null, filesToDelete, true); } public static VirtualFile createTestProjectStructure(Project project, Module module, String rootPath, Collection<File> filesToDelete, boolean addProjectRoots) throws IOException { VirtualFile vDir = createTestProjectStructure(module, rootPath, filesToDelete, addProjectRoots); PsiDocumentManager.getInstance(project).commitAllDocuments(); return vDir; } public static VirtualFile createTestProjectStructure(Module module, String rootPath, Collection<File> filesToDelete, boolean addProjectRoots) throws IOException { return createTestProjectStructure("unitTest", module, rootPath, filesToDelete, addProjectRoots); } public static VirtualFile createTestProjectStructure(String tempName, Module module, String rootPath, Collection<File> filesToDelete, boolean addProjectRoots) throws IOException { File dir = FileUtil.createTempDirectory(tempName, null, false); filesToDelete.add(dir); VirtualFile vDir = LocalFileSystem.getInstance().refreshAndFindFileByPath(dir.getCanonicalPath().replace(File.separatorChar, '/')); assert vDir != null && vDir.isDirectory() : dir; PlatformTestCase.synchronizeTempDirVfs(vDir); EdtTestUtil.runInEdtAndWait(() -> { WriteAction.run(() -> { if (rootPath != null) { VirtualFile vDir1 = LocalFileSystem.getInstance().findFileByPath(rootPath.replace(File.separatorChar, '/')); if (vDir1 == null) { throw new Exception(rootPath + " not found"); } VfsUtil.copyDirectory(null, vDir1, vDir, null); } if (addProjectRoots) { addSourceContentToRoots(module, vDir); } }); }); return vDir; } public static void removeAllRoots(@NotNull Module module, Sdk jdk) { ModuleRootModificationUtil.updateModel(module, model -> { model.clear(); model.setSdk(jdk); }); } public static void addSourceContentToRoots(Module module, @NotNull VirtualFile vDir) { addSourceContentToRoots(module, vDir, false); } public static void addSourceContentToRoots(Module module, @NotNull VirtualFile vDir, boolean testSource) { ModuleRootModificationUtil.updateModel(module, model -> model.addContentEntry(vDir).addSourceFolder(vDir, testSource)); } public static void addSourceRoot(Module module, VirtualFile vDir) { addSourceRoot(module, vDir, false); } public static void addSourceRoot(Module module, VirtualFile vDir, boolean isTestSource) { addSourceRoot(module, vDir, isTestSource ? JavaSourceRootType.TEST_SOURCE : JavaSourceRootType.SOURCE); } public static <P extends JpsElement> void addSourceRoot(Module module, VirtualFile vDir, @NotNull JpsModuleSourceRootType<P> rootType) { addSourceRoot(module, vDir, rootType, rootType.createDefaultProperties()); } public static <P extends JpsElement> void addSourceRoot(Module module, VirtualFile vDir, @NotNull JpsModuleSourceRootType<P> rootType, P properties) { ModuleRootModificationUtil.updateModel(module, model -> { ContentEntry entry = findContentEntry(model, vDir); if (entry == null) entry = model.addContentEntry(vDir); entry.addSourceFolder(vDir, rootType, properties); }); } @Nullable private static ContentEntry findContentEntry(ModuleRootModel rootModel, VirtualFile file) { return ContainerUtil.find(rootModel.getContentEntries(), object -> { VirtualFile entryRoot = object.getFile(); return entryRoot != null && VfsUtilCore.isAncestor(entryRoot, file, false); }); } public static ContentEntry addContentRoot(Module module, VirtualFile vDir) { ModuleRootModificationUtil.updateModel(module, model -> model.addContentEntry(vDir)); for (ContentEntry entry : ModuleRootManager.getInstance(module).getContentEntries()) { if (Comparing.equal(entry.getFile(), vDir)) { Assert.assertFalse(((ContentEntryImpl)entry).isDisposed()); return entry; } } return null; } public static void addExcludedRoot(Module module, VirtualFile dir) { ModuleRootModificationUtil.updateModel(module, model -> ApplicationManager.getApplication().runReadAction(() -> { findContentEntryWithAssertion(model, dir).addExcludeFolder(dir); })); } @NotNull private static ContentEntry findContentEntryWithAssertion(ModifiableRootModel model, VirtualFile dir) { ContentEntry entry = findContentEntry(model, dir); if (entry == null) { throw new RuntimeException(dir + " is not under content roots: " + Arrays.toString(model.getContentRoots())); } return entry; } public static void removeContentEntry(Module module, VirtualFile contentRoot) { ModuleRootModificationUtil.updateModel(module, model -> model.removeContentEntry(findContentEntryWithAssertion(model, contentRoot))); } public static void removeSourceRoot(Module module, VirtualFile root) { ModuleRootModificationUtil.updateModel(module, model -> { ContentEntry entry = findContentEntryWithAssertion(model, root); for (SourceFolder sourceFolder : entry.getSourceFolders()) { if (root.equals(sourceFolder.getFile())) { entry.removeSourceFolder(sourceFolder); break; } } }); } public static void removeExcludedRoot(Module module, VirtualFile root) { ModuleRootModificationUtil.updateModel(module, model -> { ContentEntry entry = findContentEntryWithAssertion(model, root); entry.removeExcludeFolder(root.getUrl()); }); } public static void checkFileStructure(PsiFile file) throws IncorrectOperationException { String originalTree = DebugUtil.psiTreeToString(file, false); PsiFile dummyFile = PsiFileFactory.getInstance(file.getProject()).createFileFromText(file.getName(), file.getFileType(), file.getText()); String reparsedTree = DebugUtil.psiTreeToString(dummyFile, false); Assert.assertEquals(reparsedTree, originalTree); } public static void addLibrary(Module module, String libPath) { File file = new File(libPath); String libName = file.getName(); addLibrary(module, libName, file.getParent(), libName); } public static void addLibrary(Module module, String libName, String libPath, String... jarArr) { ModuleRootModificationUtil.updateModel(module, model -> addLibrary(module, model, libName, libPath, jarArr)); } public static void addProjectLibrary(Module module, String libName, VirtualFile... classesRoots) { addProjectLibrary(module, libName, Arrays.asList(classesRoots), Collections.emptyList()); } public static Library addProjectLibrary(Module module, String libName, List<VirtualFile> classesRoots, List<VirtualFile> sourceRoots) { Ref<Library> result = Ref.create(); ModuleRootModificationUtil.updateModel(module, model -> result.set(addProjectLibrary(module, model, libName, classesRoots, sourceRoots))); return result.get(); } private static Library addProjectLibrary(Module module, ModifiableRootModel model, String libName, List<VirtualFile> classesRoots, List<VirtualFile> sourceRoots) { LibraryTable libraryTable = ProjectLibraryTable.getInstance(module.getProject()); RunResult<Library> result = new WriteAction<Library>() { @Override protected void run(@NotNull Result<Library> result) throws Throwable { Library library = libraryTable.createLibrary(libName); Library.ModifiableModel libraryModel = library.getModifiableModel(); try { for (VirtualFile root : classesRoots) { libraryModel.addRoot(root, OrderRootType.CLASSES); } for (VirtualFile root : sourceRoots) { libraryModel.addRoot(root, OrderRootType.SOURCES); } libraryModel.commit(); } catch (Throwable t) { //noinspection SSBasedInspection libraryModel.dispose(); throw t; } model.addLibraryEntry(library); OrderEntry[] orderEntries = model.getOrderEntries(); OrderEntry last = orderEntries[orderEntries.length - 1]; System.arraycopy(orderEntries, 0, orderEntries, 1, orderEntries.length - 1); orderEntries[0] = last; model.rearrangeOrderEntries(orderEntries); result.setResult(library); } }.execute(); result.throwException(); return result.getResultObject(); } public static void addLibrary(Module module, ModifiableRootModel model, String libName, String libPath, String... jarArr) { List<VirtualFile> classesRoots = new ArrayList<>(); for (String jar : jarArr) { if (!libPath.endsWith("/") && !jar.startsWith("/")) { jar = "/" + jar; } String path = libPath + jar; VirtualFile root; if (path.endsWith(".jar")) { root = JarFileSystem.getInstance().refreshAndFindFileByPath(path + "!/"); } else { root = LocalFileSystem.getInstance().refreshAndFindFileByPath(path); } assert root != null : "Library root folder not found: " + path + "!/"; classesRoots.add(root); } addProjectLibrary(module, model, libName, classesRoots, Collections.emptyList()); } public static void addLibrary(Module module, String libName, String libDir, String[] classRoots, String[] sourceRoots) { String proto = (classRoots.length > 0 ? classRoots[0] : sourceRoots[0]).endsWith(".jar!/") ? JarFileSystem.PROTOCOL : LocalFileSystem.PROTOCOL; String parentUrl = VirtualFileManager.constructUrl(proto, libDir); List<String> classesUrls = new ArrayList<>(); List<String> sourceUrls = new ArrayList<>(); for (String classRoot : classRoots) { classesUrls.add(parentUrl + classRoot); } for (String sourceRoot : sourceRoots) { sourceUrls.add(parentUrl + sourceRoot); } ModuleRootModificationUtil.addModuleLibrary(module, libName, classesUrls, sourceUrls); } public static Module addModule(Project project, ModuleType type, String name, VirtualFile root) { return new WriteCommandAction<Module>(project) { @Override protected void run(@NotNull Result<Module> result) throws Throwable { String moduleName; ModifiableModuleModel moduleModel = ModuleManager.getInstance(project).getModifiableModel(); try { moduleName = moduleModel.newModule(root.getPath() + "/" + name + ".iml", type.getId()).getName(); moduleModel.commit(); } catch (Throwable t) { moduleModel.dispose(); throw t; } Module dep = ModuleManager.getInstance(project).findModuleByName(moduleName); assert dep != null : moduleName; ModifiableRootModel model = ModuleRootManager.getInstance(dep).getModifiableModel(); try { model.addContentEntry(root).addSourceFolder(root, false); model.commit(); } catch (Throwable t) { model.dispose(); throw t; } result.setResult(dep); } }.execute().getResultObject(); } public static void setCompilerOutputPath(Module module, String url, boolean forTests) { ModuleRootModificationUtil.updateModel(module, model -> { CompilerModuleExtension extension = model.getModuleExtension(CompilerModuleExtension.class); extension.inheritCompilerOutputPath(false); if (forTests) { extension.setCompilerOutputPathForTests(url); } else { extension.setCompilerOutputPath(url); } }); } public static void setExcludeCompileOutput(Module module, boolean exclude) { ModuleRootModificationUtil.updateModel(module, model -> model.getModuleExtension(CompilerModuleExtension.class).setExcludeOutput(exclude)); } public static void setJavadocUrls(Module module, String... urls) { ModuleRootModificationUtil.updateModel(module, model -> model.getModuleExtension(JavaModuleExternalPaths.class).setJavadocUrls(urls)); } @NotNull @Contract(pure=true) public static Sdk addJdkAnnotations(@NotNull Sdk sdk) { String path = FileUtil.toSystemIndependentName(PlatformTestUtil.getCommunityPath()) + "/java/jdkAnnotations"; VirtualFile root = LocalFileSystem.getInstance().findFileByPath(path); return addRootsToJdk(sdk, AnnotationOrderRootType.getInstance(), root); } @NotNull @Contract(pure=true) public static Sdk addRootsToJdk(@NotNull Sdk sdk, @NotNull OrderRootType rootType, @NotNull VirtualFile... roots) { Sdk clone; try { clone = (Sdk)sdk.clone(); } catch (CloneNotSupportedException e) { throw new RuntimeException(e); } SdkModificator sdkModificator = clone.getSdkModificator(); for (VirtualFile root : roots) { sdkModificator.addRoot(root, rootType); } sdkModificator.commitChanges(); return clone; } }