/*
* Copyright 2003-2011 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 jetbrains.mps.workbench;
import com.intellij.ide.impl.ProjectUtil;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.project.Project;
import jetbrains.mps.ide.ThreadUtils;
import jetbrains.mps.project.MPSExtentions;
import jetbrains.mps.project.Solution;
import jetbrains.mps.smodel.Language;
import jetbrains.mps.smodel.LanguageAspect;
import jetbrains.mps.tool.environment.Environment;
import jetbrains.mps.tool.environment.EnvironmentConfig;
import jetbrains.mps.tool.environment.IdeaEnvironment;
import jetbrains.mps.util.CollectionUtil;
import jetbrains.mps.util.Reference;
import jetbrains.mps.vfs.IFile;
import jetbrains.mps.vfs.IFileUtils;
import jetbrains.mps.workbench.dialogs.project.newproject.ProjectFactory;
import jetbrains.mps.workbench.dialogs.project.newproject.ProjectFactory.ProjectNotCreatedException;
import jetbrains.mps.workbench.dialogs.project.newproject.ProjectOptions;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.StringJoiner;
/**
* AP:
* A little incorrect test which checks the output xml config files in the project directory written by idea platform (like misc.xml etc.)
* The problem is that the IDEA platform tends to get rid of xml configuration files in the project directory
* on the empty project creation.
* So the list of xml configuration files is not invariant and this test does not make much sense.
* <p>
* Added on Oct 16, 2010
*
* @author Evgeny Gerashchenko
*/
public class ProjectCreationTest {
private static final String PROJECT_NAME = "CreatedTestProject";
private static final String LANGUAGE_NAMESPACE = "CreatedLanguage";
private static final String SOLUTION_NAMESPACE = "CreatedSandbox";
private static final String PROJECT_PROPERTIES_DIR = PROJECT_NAME + "/.mps";
private static final List<String> PROJECT_PROPERTIES_DIR_CONTENT = Arrays.asList(
PROJECT_PROPERTIES_DIR + "/workspace.xml",
PROJECT_PROPERTIES_DIR + "/version.xml",
PROJECT_PROPERTIES_DIR + "/modules.xml",
PROJECT_PROPERTIES_DIR + "/misc.xml");
private static final List<String> EMPTY_PROJECT_PATH_LIST_FB = Arrays.asList(
PROJECT_NAME + "/" + PROJECT_NAME + ".iws", PROJECT_NAME + "/" + PROJECT_NAME + MPSExtentions.DOT_MPS_PROJECT);
private static final List<String> EMPTY_PROJECT_PATH_LIST_DB = PROJECT_PROPERTIES_DIR_CONTENT;
static final String LANGUAGES_ROOT = "languages";
static final String SOLUTIONS_ROOT = "solutions";
private static List<String> PROJECT_WITH_MODULES_PATH_LIST_TEMPLATE;
private static List<String> PROJECT_WITH_MODULES_PATH_LIST_FB;
private static List<String> PROJECT_WITH_MODULES_PATH_LIST_DB;
// project/root/module/model_source_root/
private static final String PATH_IN_PROJECT = "%s/%s/%s/%s/%s.%s";
private static Environment ourEnvironment;
private static List<String> languageModels(String projectName, String languageNamespace) {
final LanguageAspect[] aspects = new LanguageAspect[]{
LanguageAspect.STRUCTURE, LanguageAspect.CONSTRAINTS, LanguageAspect.EDITOR, LanguageAspect.BEHAVIOR, LanguageAspect.TYPESYSTEM
};
String[] rv = new String[aspects.length + 1];
for (int i = 0; i < aspects.length; i++) {
rv[i] =
String.format(PATH_IN_PROJECT, projectName, LANGUAGES_ROOT, languageNamespace, Language.LANGUAGE_MODELS, aspects[i].getName(), MPSExtentions.MODEL);
}
rv[aspects.length] =
String.format(PATH_IN_PROJECT, projectName, LANGUAGES_ROOT, languageNamespace, "generator/template", "main@generator", MPSExtentions.MODEL);
return Arrays.asList(rv);
}
private static List<String> solutionModels(String projectName, String solutionNamespace) {
return Collections.singletonList(
String.format(PATH_IN_PROJECT, projectName, SOLUTIONS_ROOT, solutionNamespace, Solution.SOLUTION_MODELS, "sandbox", MPSExtentions.MODEL));
}
private IFile myTmpDir;
private Project myProject;
@BeforeClass
public static void init() {
ourEnvironment = IdeaEnvironment.getOrCreate(EnvironmentConfig.defaultConfig());
PROJECT_WITH_MODULES_PATH_LIST_TEMPLATE = new ArrayList<>();
final String languageModule = PROJECT_NAME + "/" + LANGUAGES_ROOT + "/" + LANGUAGE_NAMESPACE + "/" + LANGUAGE_NAMESPACE + MPSExtentions.DOT_LANGUAGE;
final String solutionModule = PROJECT_NAME + "/" + SOLUTIONS_ROOT + "/" + SOLUTION_NAMESPACE + "/" + SOLUTION_NAMESPACE + MPSExtentions.DOT_SOLUTION;
PROJECT_WITH_MODULES_PATH_LIST_TEMPLATE.add(languageModule);
PROJECT_WITH_MODULES_PATH_LIST_TEMPLATE.add(solutionModule);
PROJECT_WITH_MODULES_PATH_LIST_TEMPLATE.addAll(languageModels(PROJECT_NAME, LANGUAGE_NAMESPACE));
PROJECT_WITH_MODULES_PATH_LIST_TEMPLATE.addAll(solutionModels(PROJECT_NAME, SOLUTION_NAMESPACE));
PROJECT_WITH_MODULES_PATH_LIST_FB = CollectionUtil.union(
Arrays.asList(PROJECT_NAME + "/" + PROJECT_NAME + ".iws", PROJECT_NAME + "/" + PROJECT_NAME + MPSExtentions.DOT_MPS_PROJECT),
PROJECT_WITH_MODULES_PATH_LIST_TEMPLATE);
PROJECT_WITH_MODULES_PATH_LIST_DB = CollectionUtil.union(PROJECT_PROPERTIES_DIR_CONTENT, PROJECT_WITH_MODULES_PATH_LIST_TEMPLATE);
}
@AfterClass
public static void dispose() {
ourEnvironment.release();
}
@Test
public void emptyProjectFileBased() {
invokeTest(new EmptyProjectProvider(true), EMPTY_PROJECT_PATH_LIST_FB);
}
@Test
public void emptyProjectDirectoryBased() {
invokeTest(new EmptyProjectProvider(false), EMPTY_PROJECT_PATH_LIST_DB);
}
@Test
public void projectWithModulesFileBased() {
invokeTest(new ProjectWithModulesProvider(true), PROJECT_WITH_MODULES_PATH_LIST_FB);
}
@Test
public void projectWithModulesDirectoryBased() {
invokeTest(new ProjectWithModulesProvider(false), PROJECT_WITH_MODULES_PATH_LIST_DB);
}
private void invokeTest(final ProjectOptionsProvider projectOptionsProvider, List<String> expectedPathList) {
final Reference<Throwable> refThrowable = new Reference<>();
ApplicationManager.getApplication().invokeAndWait(new Runnable() {
@Override
public void run() {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
try {
myTmpDir = IFileUtils.createTmpDir();
try {
ProjectFactory factory = new ProjectFactory(projectOptionsProvider.getProjectOptions(myTmpDir));
myProject = factory.createProject();
factory.activate();
myProject.save();
} catch (ProjectNotCreatedException e) {
Assert.fail();
}
} catch (Throwable t) {
refThrowable.set(t);
}
}
});
}
}, ModalityState.defaultModalityState());
if (!refThrowable.isNull()) {
throw new RuntimeException(refThrowable.get());
}
Exception exception = ThreadUtils.runInUIThreadAndWait(new Runnable() {
@Override
public void run() {
try {
ProjectUtil.closeAndDispose(myProject);
} catch (Throwable t) {
refThrowable.set(t);
}
}
});
if (exception != null) {
throw new RuntimeException(exception);
}
checkFilePathList(myTmpDir, expectedPathList);
}
private static void checkFilePathList(IFile rootDir, List<String> expectedList) {
List<String> actualList = collectFilePathList(rootDir);
Set<String> missing = new HashSet<>(expectedList);
missing.removeAll(actualList);
Set<String> unexpected = new HashSet<>(actualList);
unexpected.removeAll(expectedList);
StringJoiner missingString = new StringJoiner("\n");
missing.forEach(missingString::add);
StringJoiner unexpectedString = new StringJoiner("\n");
unexpected.forEach(unexpectedString::add);
Assert.assertTrue("\nMissing files:\n" + missingString + "\nUnexpected files:\n" + unexpectedString, missing.isEmpty() && unexpected.isEmpty());
}
private static List<String> collectFilePathList(IFile rootDir) {
ArrayList<String> currentList = new ArrayList<>();
collectFilePathList(currentList, rootDir, null);
return currentList;
}
private static void collectFilePathList(List<String> list, IFile rootDir, String prefix) {
for (IFile file : rootDir.getChildren()) {
String path = file.getName();
if (prefix != null) {
path = prefix + "/" + file.getName();
}
if (file.isDirectory() && !file.getChildren().isEmpty()) {
collectFilePathList(list, file, path);
} else {
list.add(path);
}
}
}
private interface ProjectOptionsProvider {
ProjectOptions getProjectOptions(IFile containingFile);
}
private static class EmptyProjectProvider implements ProjectOptionsProvider {
private boolean myDefaultScheme;
public EmptyProjectProvider(boolean defaultScheme) {
myDefaultScheme = defaultScheme;
}
@Override
public ProjectOptions getProjectOptions(IFile containingDir) {
ProjectOptions options = new ProjectOptions();
options.setProjectName(PROJECT_NAME);
options.setProjectPath(containingDir.getDescendant(PROJECT_NAME).getPath());
options.setStorageScheme(myDefaultScheme);
options.setCreateNewLanguage(false);
options.setCreateNewSolution(false);
options.setCreateModel(false);
return options;
}
}
private static class ProjectWithModulesProvider implements ProjectOptionsProvider {
private boolean myDefaultScheme;
public ProjectWithModulesProvider(boolean defaultScheme) {
myDefaultScheme = defaultScheme;
}
@Override
public ProjectOptions getProjectOptions(IFile containingFile) {
IFile projectDir = containingFile.getDescendant(PROJECT_NAME);
ProjectOptions options = new ProjectOptions();
options.setProjectName(PROJECT_NAME);
options.setProjectPath(projectDir.getPath());
options.setStorageScheme(myDefaultScheme);
options.setCreateNewLanguage(true);
options.setLanguageNamespace(LANGUAGE_NAMESPACE);
options.setLanguagePath(projectDir.getDescendant(LANGUAGES_ROOT).getDescendant(LANGUAGE_NAMESPACE).getPath());
options.setCreateNewSolution(true);
options.setSolutionNamespace(SOLUTION_NAMESPACE);
options.setSolutionPath(projectDir.getDescendant(SOLUTIONS_ROOT).getDescendant(SOLUTION_NAMESPACE).getPath());
options.setCreateModel(true);
return options;
}
}
}