// Copyright 2014 Pants project contributors (see CONTRIBUTORS.md).
// Licensed under the Apache License, Version 2.0 (see LICENSE).
package com.twitter.intellij.pants.service.project;
import com.intellij.openapi.externalSystem.model.DataNode;
import com.intellij.openapi.externalSystem.model.ProjectKeys;
import com.intellij.openapi.externalSystem.model.project.ContentRootData;
import com.intellij.openapi.externalSystem.model.project.LibraryDependencyData;
import com.intellij.openapi.externalSystem.model.project.ModuleData;
import com.intellij.openapi.externalSystem.model.project.ModuleDependencyData;
import com.intellij.openapi.externalSystem.model.project.ProjectData;
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import com.twitter.intellij.pants.model.PantsSourceType;
import com.twitter.intellij.pants.model.TargetAddressInfo;
import com.twitter.intellij.pants.service.PantsCompileOptionsExecutor;
import com.twitter.intellij.pants.service.project.model.ContentRoot;
import com.twitter.intellij.pants.service.project.model.LibraryInfo;
import com.twitter.intellij.pants.service.project.model.ProjectInfo;
import com.twitter.intellij.pants.service.project.model.TargetInfo;
import com.twitter.intellij.pants.testFramework.PantsCodeInsightFixtureTestCase;
import com.twitter.intellij.pants.util.PantsConstants;
import com.twitter.intellij.pants.util.PantsUtil;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
abstract class PantsResolverTestBase extends PantsCodeInsightFixtureTestCase {
private Map<String, TargetInfoBuilder> myInfoBuilders = null;
@Nullable
private DataNode<ProjectData> myProjectNode;
@Override
protected void setUp() throws Exception {
super.setUp();
myProjectNode = null;
myInfoBuilders = new HashMap<>();
}
@Override
protected void tearDown() throws Exception {
myProjectNode = null;
myInfoBuilders = null;
super.tearDown();
}
@NotNull
protected DataNode<ProjectData> getProjectNode() {
if (myProjectNode == null) {
myProjectNode = createProjectNode();
}
return myProjectNode;
}
protected DataNode<ProjectData> createProjectNode() {
final PantsResolver dependenciesResolver = new PantsResolver(PantsCompileOptionsExecutor.createMock());
dependenciesResolver.setProjectInfo(getProjectInfo());
final ProjectData projectData = new ProjectData(
PantsConstants.SYSTEM_ID, "test-project", "path/to/fake/project", "path/to/fake/project/BUILD"
);
final DataNode<ProjectData> dataNode = new DataNode<ProjectData>(ProjectKeys.PROJECT, projectData, null);
dependenciesResolver.addInfoTo(dataNode);
return dataNode;
}
private ProjectInfo getProjectInfo() {
final ProjectInfo result = new ProjectInfo();
result.setTargets(
PantsUtil.mapValues(
myInfoBuilders,
new Function<TargetInfoBuilder, TargetInfo>() {
@Override
public TargetInfo fun(TargetInfoBuilder builder) {
return builder.build();
}
}
)
);
final Map<String, LibraryInfo> libraries = new HashMap<String, LibraryInfo>();
for (TargetInfo targetInfo : result.getTargets().values()) {
for (String libraryId : targetInfo.getLibraries()) {
libraries.put(libraryId, new LibraryInfo(libraryId.replace('.', File.separatorChar)));
}
}
result.setLibraries(libraries);
return result;
}
protected TargetInfoBuilder addJarLibrary(String name) {
return addInfo(name).withType("jar_library").withLibrary("name");
}
protected TargetInfoBuilder addInfo(String name) {
final TargetInfoBuilder result = new TargetInfoBuilder();
myInfoBuilders.put(name, result);
return result;
}
public void assertDependency(String moduleName, final String dependencyName) {
final DataNode<ModuleData> moduleNode = findModule(moduleName);
assertModuleExists(moduleName, moduleNode);
final Collection<DataNode<ModuleDependencyData>> dependencies =
ExternalSystemApiUtil.findAll(moduleNode, ProjectKeys.MODULE_DEPENDENCY);
final DataNode<ModuleDependencyData> dependencyDataNode = ContainerUtil.find(
dependencies,
new Condition<DataNode<ModuleDependencyData>>() {
@Override
public boolean value(DataNode<ModuleDependencyData> node) {
return StringUtil.equalsIgnoreCase(dependencyName, node.getData().getExternalName());
}
}
);
List<String> actualDependencyNames = ContainerUtil.map(
dependencies, new Function<DataNode<ModuleDependencyData>, String>() {
@Override
public String fun(DataNode<ModuleDependencyData> node) {
return node.getData().getExternalName();
}
}
);
assertNotNull(
String.format("%s doesn't have a dependency %s in list %s", moduleName, dependencyName, actualDependencyNames.toString()),
dependencyDataNode
);
}
public void assertSourceRoot(String moduleName, final String path) {
final DataNode<ModuleData> moduleNode = findModule(moduleName);
assertModuleExists(moduleName, moduleNode);
final Collection<DataNode<ContentRootData>> contentRoots = ExternalSystemApiUtil.findAll(moduleNode, ProjectKeys.CONTENT_ROOT);
assertFalse(String.format("No content root for module %s", moduleName), contentRoots.isEmpty());
for (DataNode<ContentRootData> contentRoot : contentRoots) {
final ContentRootData contentRootData = contentRoot.getData();
for (PantsSourceType type : PantsSourceType.values()) {
for (ContentRootData.SourceRoot sourceRoot : contentRootData.getPaths(type.toExternalSystemSourceType())) {
final File expectedFile = new File(new File(""), path);
if (StringUtil.equalsIgnoreCase(expectedFile.getPath(), sourceRoot.getPath())) {
return;
}
}
}
}
fail(String.format("Source root %s is not found for %s!", path, moduleName));
}
public void assertNoContentRoot(String moduleName) {
final DataNode<ModuleData> moduleNode = findModule(moduleName);
assertModuleExists(moduleName, moduleNode);
final Collection<DataNode<ContentRootData>> contentRoots = ExternalSystemApiUtil.findAll(moduleNode, ProjectKeys.CONTENT_ROOT);
for (DataNode<ContentRootData> contentRoot : contentRoots) {
assertNull(
String
.format("Content root %s is defined for module %s", contentRoot.getData().getRootPath(), moduleName),
contentRoot
);
}
}
public void assertContentRoots(String moduleName, String... roots) {
final DataNode<ModuleData> moduleNode = findModule(moduleName);
assertModuleExists(moduleName, moduleNode);
final Collection<DataNode<ContentRootData>> contentRoots = ExternalSystemApiUtil.findAll(moduleNode, ProjectKeys.CONTENT_ROOT);
final List<String> actualRootPaths = contentRoots.stream().map(s -> s.getData().getRootPath()).collect(Collectors.toList());
List<String> expected = Arrays.asList(roots);
Collections.sort(expected);
Collections.sort(actualRootPaths);
assertEquals("Content roots", expected, actualRootPaths);
}
private void assertModuleExists(@NotNull String moduleName, @Nullable DataNode<ModuleData> moduleNode) {
assertNotNull(String.format("Module %s is missing!", moduleName), moduleNode);
}
public void assertLibrary(String moduleName, final String libraryId) {
final DataNode<LibraryDependencyData> dependencyDataNode = findLibraryDependency(moduleName, libraryId);
assertNotNull(String.format("%s doesn't have a dependency %s", moduleName, libraryId), dependencyDataNode);
}
@Nullable
public DataNode<LibraryDependencyData> findLibraryDependency(String moduleName, final String libraryId) {
final DataNode<ModuleData> moduleNode = findModule(moduleName);
return ExternalSystemApiUtil.findAll(moduleNode, ProjectKeys.LIBRARY_DEPENDENCY)
.stream()
.filter(node -> StringUtil.equalsIgnoreCase(libraryId, node.getData().getExternalName()))
.findFirst()
.orElse(null);
}
@Nullable
private DataNode<ModuleData> findModule(final String moduleName) {
final Collection<DataNode<ModuleData>> moduleNodes = ExternalSystemApiUtil.findAll(getProjectNode(), ProjectKeys.MODULE);
return moduleNodes.stream()
.filter(node -> StringUtil.equalsIgnoreCase(moduleName, node.getData().getExternalName()))
.findFirst()
.orElse(null);
}
public void assertModulesCreated(final String... expectedModules) {
final Collection<DataNode<ModuleData>> moduleNodes = ExternalSystemApiUtil.findAll(getProjectNode(), ProjectKeys.MODULE);
final Set<String> actualModules = moduleNodes.stream().map(n -> n.getData().getExternalName()).collect(Collectors.toSet());
assertEquals(Arrays.stream(expectedModules).collect(Collectors.toSet()), actualModules);
}
private static interface Builder<T> {
T build();
}
public static final class TargetInfoBuilder implements Builder<TargetInfo> {
private String type = "scala_library";
private Set<String> libraries = new HashSet<String>();
private Set<String> excludes = new HashSet<String>();
private Set<String> targets = new HashSet<String>();
private Set<ContentRoot> roots = new HashSet<ContentRoot>();
@Override
public TargetInfo build() {
final TargetAddressInfo addressInfo = new TargetAddressInfo();
addressInfo.setPantsTargetType(type);
return new TargetInfo(Collections.singleton(addressInfo), targets, libraries, excludes, roots);
}
public TargetInfoBuilder withType(@Nls String targetType) {
type = targetType;
return this;
}
public TargetInfoBuilder withRoot(@Nls String rootRelativePath, @NotNull String packagePrefix) {
final File root = new File(new File(""), rootRelativePath);
roots.add(new ContentRoot(root.getAbsolutePath(), packagePrefix));
return this;
}
public TargetInfoBuilder withLibrary(@Nls String libraryId) {
targets.add(libraryId);
return this;
}
public TargetInfoBuilder withDependency(@Nls String targetName) {
targets.add(targetName);
return this;
}
}
}