/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.plugin.ij.framework.core;
import com.intellij.ide.highlighter.ModuleFileType;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.application.WriteAction;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.module.ModuleType;
import com.intellij.openapi.module.StdModuleTypes;
import com.intellij.openapi.module.impl.ModuleImpl;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.impl.ProjectImpl;
import com.intellij.openapi.roots.ContentEntry;
import com.intellij.openapi.roots.ModifiableRootModel;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.roots.impl.ModuleRootManagerImpl;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.testFramework.PsiTestUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Set;
public abstract class ModuleTestCase extends IdeaTestCase {
// protected final Collection<Module> myModulesToDispose = new ArrayList<Module>();
protected final HashMap<String, LinkedList<String>> _myModuleDependencies = new HashMap<>();
@Override
protected void afterClass() throws Exception {
if (myProject == null) {
return;
}
try {
final ModuleManager moduleManager = ModuleManager.getInstance(myProject);
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
for (Module module : moduleManager.getModules()) {
String moduleName = module.getName();
if (moduleManager.findModuleByName(moduleName) != null) {
if (!module.isDisposed()) {
moduleManager.disposeModule(module);
}
}
}
}
});
} catch (Exception e) {
e.printStackTrace();
} finally {
super.afterClass();
}
}
@Override
protected void beforeMethod() throws Exception {
super.beforeMethod();
// myModulesToDispose.clear();
}
@Override
protected void afterMethod() throws Exception {
// myModulesToDispose.clear();
super.afterMethod();
}
protected void initModules(@NotNull String[] moduleWithDependencies) {
_myModuleDependencies.clear();
for (String module : moduleWithDependencies) {
String[] mdepend = module.split("<");
assertTrue("mismatched module data format: ModuleName<DependentModuleName1,DependentModuleName2", mdepend.length == 1 || mdepend.length == 2);
assertTrue("empty module name not allowed", mdepend[0].length() > 0);
LinkedList<String> mdlist = new LinkedList<>();
if (mdepend.length == 2) {
for (String m : mdepend[1].split(",")) {
assertTrue("empty module name not allowed", m.length() > 0);
mdlist.add(m);
}
}
_myModuleDependencies.put(mdepend[0], mdlist);
}
// module dependency cycle detection
for (String module : _myModuleDependencies.keySet()) {
LinkedList<String> list = _myModuleDependencies.get(module);
Set<String> visited = new HashSet<>();
visited.add(module);
detectLoop(visited, list);
}
}
private void detectLoop(@NotNull Set<String> visited, @NotNull LinkedList<String> list) {
for (String m : list) {
assertFalse("module cycle dependency detected.", visited.contains(m));
visited.add(m);
Object childList = _myModuleDependencies.get(m);
if (childList != null && ((LinkedList<String>) childList).size() > 0) {
detectLoop(visited, (LinkedList<String>) childList);
}
}
}
@Override
protected void setUpModule() {
// a switch from single main module test case to multiple modules test case
if (_myModuleDependencies == null || _myModuleDependencies.isEmpty()) {
// set up default single module
super.setUpModule();
} else {
// set up multiple modules and module dependencies if available
for (String moduleName : _myModuleDependencies.keySet()) {
createModule(moduleName);
}
for (String module : _myModuleDependencies.keySet()) {
LinkedList<String> moduleDepends = _myModuleDependencies.get(module);
for (String moduleDepend : moduleDepends) {
addModuleDependency(module, moduleDepend);
}
}
}
}
@Override
protected Module createModule(@NonNls final String moduleName) {
final Module m = super.createModule(moduleName);
// module m is created with
// getProject().getBaseDir().getPath().replace('/', File.separatorChar) + File.separatorChar +
// moduleName + ModuleFileType.DOT_DEFAULT_EXTENSION
new WriteAction<ModifiableRootModel>() {
@Override
protected void run(@NotNull Result<ModifiableRootModel> result) throws Throwable {
// add content root and source root
final VirtualFile baseDir = getProject().getBaseDir();
assertNotNull(baseDir);
baseDir.refresh(false, false);
VirtualFile moduleDir = baseDir.createChildDirectory(this, moduleName);
moduleDir.refresh(false, false);
VirtualFile srcDir = moduleDir.createChildDirectory(this, "src");
srcDir.refresh(false, false);
final ModifiableRootModel rootModel = ModuleRootManager.getInstance(m).getModifiableModel();
final ContentEntry contentRoot = rootModel.addContentEntry(moduleDir);
contentRoot.addSourceFolder(srcDir, false);
rootModel.commit();
result.setResult(rootModel);
}
}.execute().getResultObject();
// alternatively, use:
// try {
// final VirtualFile baseDir = getProject().getBaseDir();
// assertNotNull(baseDir);
// baseDir.refresh(false, false);
// VirtualFile moduleDir = null;
// moduleDir = baseDir.createChildDirectory(this, moduleName);
// moduleDir.refresh(false, false);
// addSourceContentToRoots (m, moduleDir);
// } catch (IOException e) {
//
// }
// myModulesToDispose.add(m);
return m;
}
protected void addModuleDependency(@NotNull String module, @NotNull String moduleDepend) {
final Module m = getModuleByName(module);
final Module md = getModuleByName(moduleDepend);
new WriteCommandAction(getProject()) {
@Override
protected void run(Result result) throws Throwable {
ModifiableRootModel mRoot = ModuleRootManager.getInstance(m).getModifiableModel();
mRoot.addModuleOrderEntry(md);
mRoot.commit();
}
}.execute();
}
@Nullable
protected Module getModuleByName(@NotNull String module) {
Module m = ModuleManager.getInstance(getProject()).findModuleByName(module);
assertNotNull("Module " + module + " does not exist", m);
return m;
}
protected Module createModule(@NotNull final File moduleFile) {
return createModule(moduleFile, StdModuleTypes.JAVA);
}
protected Module createModule(@NotNull final File moduleFile, @NotNull final ModuleType moduleType) {
final String path = moduleFile.getAbsolutePath();
return createModule(path, moduleType);
}
protected Module createModule(@NotNull final String path, @NotNull final ModuleType moduleType) {
Module module = ApplicationManager.getApplication().runWriteAction(
new Computable<Module>() {
@NotNull
@Override
public Module compute() {
return ModuleManager.getInstance(myProject).newModule(path, moduleType.getId());
}
}
);
// myModulesToDispose.add(module);
return module;
}
protected Module loadModule(@NotNull final File moduleFile) {
Module module = ApplicationManager.getApplication().runWriteAction(
new Computable<Module>() {
@Nullable
@Override
public Module compute() {
try {
return ModuleManager.getInstance(myProject).loadModule(moduleFile.getAbsolutePath());
} catch (Exception e) {
LOG.error(e);
return null;
}
}
}
);
// myModulesToDispose.add(module);
return module;
}
public Module loadModule(final String modulePath, Project project) {
return loadModule(new File(modulePath));
}
@Nullable
protected ModuleImpl loadAllModulesUnder(@NotNull VirtualFile rootDir) throws Exception {
ModuleImpl module = null;
final VirtualFile[] children = rootDir.getChildren();
for (VirtualFile child : children) {
if (child.isDirectory()) {
final ModuleImpl childModule = loadAllModulesUnder(child);
if (module == null) module = childModule;
} else if (child.getName().endsWith(ModuleFileType.DOT_DEFAULT_EXTENSION)) {
String modulePath = child.getPath();
module = (ModuleImpl) loadModule(new File(modulePath));
readJdomExternalizables(module);
}
}
return module;
}
protected void readJdomExternalizables(@NotNull final ModuleImpl module) {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
public void run() {
final ProjectImpl project = (ProjectImpl) myProject;
project.setOptimiseTestLoadSpeed(false);
final ModuleRootManagerImpl moduleRootManager = (ModuleRootManagerImpl) ModuleRootManager.getInstance(module);
module.getStateStore().initComponent(moduleRootManager, false);
project.setOptimiseTestLoadSpeed(true);
}
});
}
protected Module createModuleFromTestData(final String dirInTestData, final String newModuleFileName, @NotNull final ModuleType moduleType,
final boolean addSourceRoot)
throws IOException {
final File dirInTestDataFile = new File(dirInTestData);
assertTrue(dirInTestDataFile.isDirectory());
final File moduleDir = createTempDirectory();
FileUtil.copyDir(dirInTestDataFile, moduleDir);
final Module module = createModule(moduleDir + "/" + newModuleFileName, moduleType);
VirtualFile root = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(moduleDir);
if (addSourceRoot) {
PsiTestUtil.addSourceContentToRoots(module, root);
} else {
PsiTestUtil.addContentRoot(module, root);
}
return module;
}
}