navigate to testdata: do not calculate VirtualFile-s twice

This commit is contained in:
Dmitry Batkovich
2018-12-28 14:43:22 +03:00
parent 15018b5014
commit d9f7ff47bb
13 changed files with 274 additions and 164 deletions
@@ -40,7 +40,7 @@ class GotoTestDataAction extends AnAction implements Comparable {
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
RelativePoint point = JBPopupFactory.getInstance().guessBestPopupLocation(e.getDataContext());
TestDataNavigationHandler.navigate(point, Collections.singletonList(myFilePath), myProject);
TestDataNavigationHandler.navigate(point, Collections.singletonList(new TestDataFile.LazyResolved(myFilePath)), myProject);
}
@Override
@@ -42,7 +42,7 @@ public class NavigateToTestDataAction extends AnAction implements TestTreeViewAc
final RelativePoint point = editor != null ? popupFactory.guessBestPopupLocation(editor) :
popupFactory.guessBestPopupLocation(dataContext);
List<String> fileNames = findTestDataFiles(dataContext);
List<TestDataFile> fileNames = findTestDataFiles(dataContext);
if (fileNames == null || fileNames.isEmpty()) {
PsiMethod method = findTargetMethod(dataContext);
fileNames = method == null ? null : TestDataGuessByExistingFilesUtil.guessTestDataName(method);
@@ -61,7 +61,7 @@ public class NavigateToTestDataAction extends AnAction implements TestTreeViewAc
}
@Nullable
static List<String> findTestDataFiles(@NotNull DataContext context) {
static List<TestDataFile> findTestDataFiles(@NotNull DataContext context) {
final PsiMethod method = findTargetMethod(context);
if (method == null) {
PsiClass parametrizedTestClass = findParametrizedClass(context);
@@ -28,7 +28,7 @@ public class TestDataAsRelatedFileProvider extends GotoRelatedProvider {
PsiElement ctxElement = uMethod == null ? NavigateToTestDataAction.findParametrizedClass(context) : uMethod.getSourcePsi();
if (ctxElement == null) return Collections.emptyList();
List<String> testDataFiles = NavigateToTestDataAction.findTestDataFiles(context);
List<TestDataFile> testDataFiles = NavigateToTestDataAction.findTestDataFiles(context);
return testDataFiles == null || testDataFiles.isEmpty()
? Collections.emptyList()
: Collections.singletonList(new TestDataRelatedItem(ctxElement, editor, testDataFiles));
@@ -0,0 +1,117 @@
// Copyright 2000-2018 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 org.jetbrains.idea.devkit.testAssistant;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.PathUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
interface TestDataFile {
boolean exists();
@Nullable
VirtualFile getVirtualFile();
@NotNull
String getPath();
@NotNull
String getName();
class Existing implements TestDataFile {
private final VirtualFile myFile;
public Existing(VirtualFile file) {myFile = file;}
@Override
public boolean exists() {
return true;
}
@Override
public VirtualFile getVirtualFile() {
return myFile;
}
@NotNull
@Override
public String getPath() {
return myFile.getPath();
}
@NotNull
@Override
public String getName() {
return myFile.getName();
}
}
class NonExisting implements TestDataFile {
private final String myPath;
public NonExisting(String path) {myPath = path;}
@Override
public boolean exists() {
return false;
}
@Override
public VirtualFile getVirtualFile() {
return null;
}
@NotNull
@Override
public String getPath() {
return myPath;
}
@NotNull
@Override
public String getName() {
return PathUtil.getFileName(getPath());
}
}
class LazyResolved implements TestDataFile {
private final String myPath;
private VirtualFile myFile;
private boolean myResolved;
public LazyResolved(String path) {myPath = path;}
private void resolve() {
if (!myResolved) {
myResolved = true;
myFile = LocalFileSystem.getInstance().refreshAndFindFileByPath(myPath);
}
}
@Override
public boolean exists() {
resolve();
return getVirtualFile() != null;
}
@Override
public VirtualFile getVirtualFile() {
resolve();
return myFile;
}
@NotNull
@Override
public String getName() {
return PathUtil.getFileName(getPath());
}
@NotNull
@Override
public String getPath() {
return myPath;
}
}
}
@@ -19,6 +19,8 @@ import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.openapi.vfs.VirtualFileWithId;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiFileSystemItem;
import com.intellij.psi.PsiMethod;
@@ -52,22 +54,16 @@ public class TestDataGuessByExistingFilesUtil {
private TestDataGuessByExistingFilesUtil() {
}
@NotNull
static List<String> collectTestDataByExistingFiles(@NotNull PsiMethod psiMethod) {
return collectTestDataByExistingFiles(psiMethod, null);
}
/**
* Tries to guess what test data files match to the given method if it's test method and there are existing test data
* files for the target test class.
*
* @param psiMethod test method candidate
* @param testDataPath test data path if present (e.g. obtained from @TestDataPath annotation value)
* @return List of paths to the test data files for the given test if it's possible to guess them; empty List otherwise
* @return List of existing test data files for the given test if it's possible to guess them; empty List otherwise
*/
@NotNull
static List<String> collectTestDataByExistingFiles(@NotNull PsiMethod psiMethod, @Nullable String testDataPath) {
static List<TestDataFile> collectTestDataByExistingFiles(@NotNull PsiMethod psiMethod, @Nullable String testDataPath) {
Application application = ApplicationManager.getApplication();
if (!application.isUnitTestMode() && application.isHeadlessEnvironment()) {
// shouldn't be invoked under these conditions anyway, just for additional safety
@@ -75,10 +71,10 @@ public class TestDataGuessByExistingFilesUtil {
return Collections.emptyList();
}
return buildDescriptorFromExistingTestData(psiMethod, testDataPath).generate();
return buildDescriptorFromExistingTestData(psiMethod, testDataPath).restoreFiles();
}
static List<String> guessTestDataName(PsiMethod method) {
static List<TestDataFile> guessTestDataName(PsiMethod method) {
String testName = getTestName(method);
if (testName == null) return null;
PsiClass psiClass = method.getContainingClass();
@@ -87,14 +83,14 @@ public class TestDataGuessByExistingFilesUtil {
int count = 5;
PsiMethod prev = PsiTreeUtil.getPrevSiblingOfType(method, PsiMethod.class);
while (prev != null && count-- > 0) {
List<String> testData = guessTestDataBySiblingTest(prev, testDataBasePath, testName);
List<TestDataFile> testData = guessTestDataBySiblingTest(prev, testDataBasePath, testName);
if (!testData.isEmpty()) return testData;
prev = PsiTreeUtil.getPrevSiblingOfType(prev, PsiMethod.class);
}
count = 5;
PsiMethod next = PsiTreeUtil.getNextSiblingOfType(method, PsiMethod.class);
while (next != null && count-- > 0) {
List<String> testData = guessTestDataBySiblingTest(next, testDataBasePath, testName);
List<TestDataFile> testData = guessTestDataBySiblingTest(next, testDataBasePath, testName);
if (!testData.isEmpty()) return testData;
next = PsiTreeUtil.getNextSiblingOfType(next, PsiMethod.class);
}
@@ -102,8 +98,8 @@ public class TestDataGuessByExistingFilesUtil {
}
@NotNull
private static List<String> guessTestDataBySiblingTest(PsiMethod psiMethod, String testDataBasePath, String testName) {
return buildDescriptorFromExistingTestData(psiMethod, testDataBasePath).generate(testName, null);
private static List<TestDataFile> guessTestDataBySiblingTest(PsiMethod psiMethod, String testDataBasePath, String testName) {
return buildDescriptorFromExistingTestData(psiMethod, testDataBasePath).generateByTemplates(testName, null);
}
@Nullable
@@ -161,10 +157,10 @@ public class TestDataGuessByExistingFilesUtil {
return buildDescriptor(testName, psiClass, testDataPath);
}
public static List<String> suggestTestDataFiles(@NotNull String testName,
String testDataPath,
@NotNull PsiClass psiClass){
return buildDescriptor(testName, psiClass, testDataPath).generate(testName, testDataPath);
public static List<TestDataFile> suggestTestDataFiles(@NotNull String testName,
String testDataPath,
@NotNull PsiClass psiClass) {
return buildDescriptor(testName, psiClass, testDataPath).restoreFiles();
}
@NotNull
@@ -266,7 +262,7 @@ public class TestDataGuessByExistingFilesUtil {
List<TestLocationDescriptor> descriptors = ContainerUtil.flatten(descriptorsByFileNames.values());
filterDirsFromOtherModules(descriptors);
return new TestDataDescriptor(descriptors, possibleFileName);
return new TestDataDescriptor(descriptors);
}
private static Collection<String> getAllFileNames(final String testName, final GotoFileModel model) {
@@ -378,10 +374,11 @@ public class TestDataGuessByExistingFilesUtil {
}
private static class TestLocationDescriptor {
public String pathPrefix;
public String pathSuffix;
public boolean startWithLowerCase;
public boolean isFromCurrentModule;
String pathPrefix;
String pathSuffix;
boolean startWithLowerCase;
boolean isFromCurrentModule;
int matchedVFileId;
public boolean isComplete() {
return pathPrefix != null && pathSuffix != null;
@@ -407,6 +404,7 @@ public class TestDataGuessByExistingFilesUtil {
if (module != null) {
isFromCurrentModule = module.equals(ModuleUtilCore.findModuleForFile(matched, project));
}
matchedVFileId = ((VirtualFileWithId)matched).getId();
}
@Override
@@ -438,37 +436,33 @@ public class TestDataGuessByExistingFilesUtil {
}
private static class TestDataDescriptor {
private static final TestDataDescriptor NOTHING_FOUND = new TestDataDescriptor(Collections.emptyList(), null);
private static final TestDataDescriptor NOTHING_FOUND = new TestDataDescriptor(Collections.emptyList());
private final List<TestLocationDescriptor> myDescriptors = new ArrayList<>();
private final String myTestName;
TestDataDescriptor(Collection<TestLocationDescriptor> descriptors, String testName) {
myTestName = testName;
TestDataDescriptor(Collection<TestLocationDescriptor> descriptors) {
myDescriptors.addAll(descriptors);
}
@NotNull
public List<String> generate(@NotNull final String testName) {
return generate(testName, null);
public List<TestDataFile> restoreFiles() {
return ContainerUtil.mapNotNull(myDescriptors, d -> {
VirtualFile file = VirtualFileManager.getInstance().findFileById(d.matchedVFileId);
return file == null ? null : new TestDataFile.Existing(file);
});
}
@NotNull
public List<String> generate() {
return generate(myTestName, null);
}
@NotNull
public List<String> generate(@NotNull final String testName, String root) {
List<String> result = new ArrayList<>();
public List<TestDataFile> generateByTemplates(@NotNull String testName, @Nullable String root) {
List<TestDataFile> result = new ArrayList<>();
if (StringUtil.isEmpty(testName)) {
return result;
}
for (TestLocationDescriptor descriptor : myDescriptors) {
if (root != null && !descriptor.pathPrefix.startsWith(root)) continue;
result.add(descriptor.pathPrefix + (descriptor.startWithLowerCase
? StringUtil.decapitalize(testName)
: StringUtil.capitalize(testName)) + descriptor.pathSuffix);
result.add(new TestDataFile.NonExisting(descriptor.pathPrefix + (descriptor.startWithLowerCase
? StringUtil.decapitalize(testName)
: StringUtil.capitalize(testName)) + descriptor.pathSuffix));
}
return result;
}
@@ -13,6 +13,7 @@ import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiMethod;
import com.intellij.util.Consumer;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
@@ -25,7 +26,7 @@ public class TestDataGuessByTestDiscoveryUtil {
private static final Logger LOG = Logger.getInstance(TestDataGuessByTestDiscoveryUtil.class);
@NotNull
static List<String> collectTestDataByExistingFiles(@NotNull PsiMethod method) {
static List<TestDataFile> collectTestDataByExistingFiles(@NotNull PsiMethod method) {
if (!isEnabled()) return Collections.emptyList();
PsiClass testClass = method.getContainingClass();
if (testClass == null) return Collections.emptyList();
@@ -45,7 +46,7 @@ public class TestDataGuessByTestDiscoveryUtil {
}
@NotNull
static List<String> collectTestDataByExistingFiles(@NotNull PsiClass parametrizedTestClass) {
static List<TestDataFile> collectTestDataByExistingFiles(@NotNull PsiClass parametrizedTestClass) {
if (!isEnabled()) return Collections.emptyList();
String testClassQualifiedName = parametrizedTestClass.getQualifiedName();
if (testClassQualifiedName == null) return Collections.emptyList();
@@ -87,8 +88,8 @@ public class TestDataGuessByTestDiscoveryUtil {
}
@NotNull
List<String> getTestData() {
return myTestData;
List<TestDataFile> getTestData() {
return ContainerUtil.mapNotNull(myTestData, f -> new TestDataFile.LazyResolved(f));
}
}
@@ -8,11 +8,9 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.util.FontUtil;
import com.intellij.util.PathUtil;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -29,12 +27,12 @@ public class TestDataNavigationElementFactory {
}
@NotNull
public static TestDataNavigationElement createForNonExistingFile(@NotNull Project project, @NotNull String path) {
public static TestDataNavigationElement createForNonExistingFile(@NotNull Project project, @NotNull TestDataFile path) {
return new NonExistingTestDataFileNavigationElement(project, path);
}
@NotNull
public static TestDataNavigationElement createForFile(@NotNull Project project, @NotNull VirtualFile file) {
public static TestDataNavigationElement createForFile(@NotNull Project project, @NotNull TestDataFile file) {
return new TestDataFileNavigationElement(project, file);
}
@@ -44,23 +42,24 @@ public class TestDataNavigationElementFactory {
}
@NotNull
public static TestDataNavigationElement createForCreateMissingFilesOption(@NotNull List<String> filePaths) {
public static TestDataNavigationElement createForCreateMissingFilesOption(@NotNull List<TestDataFile> filePaths) {
return new CreateMissingTestDataFilesNavigationElement(filePaths);
}
private static class CreateMissingTestDataFilesNavigationElement implements TestDataNavigationElement {
private final List<String> myFilePaths;
private final List<TestDataFile> myFilePaths;
private CreateMissingTestDataFilesNavigationElement(List<String> filePaths) {
private CreateMissingTestDataFilesNavigationElement(List<TestDataFile> filePaths) {
myFilePaths = filePaths;
}
@Override
public void performAction(@NotNull Project project) {
Set<String> filePathsToCreate = new HashSet<>();
for (String path : myFilePaths) {
if (LocalFileSystem.getInstance().refreshAndFindFileByPath(path) == null) {
filePathsToCreate.add(path);
for (TestDataFile file : myFilePaths) {
VirtualFile vFile = file.getVirtualFile();
if (vFile == null || !vFile.isValid()) {
filePathsToCreate.add(file.getPath());
}
}
@@ -82,7 +81,7 @@ public class TestDataNavigationElementFactory {
}
filePathsToCreate.forEach(path -> {
VirtualFile file = TestDataUtil.createFileByName(project, path);
VirtualFile file = TestDataUtil.createFileByPath(project, path);
PsiNavigationSupport.getInstance().createNavigatable(project, file, -1).navigate(true);
});
}
@@ -169,9 +168,9 @@ public class TestDataNavigationElementFactory {
private static class NonExistingTestDataFileNavigationElement implements TestDataNavigationElement {
private final Project myProject;
private final String myPath;
private final TestDataFile myPath;
private NonExistingTestDataFileNavigationElement(@NotNull Project project, @NotNull String path) {
private NonExistingTestDataFileNavigationElement(@NotNull Project project, @NotNull TestDataFile path) {
myProject = project;
myPath = path;
}
@@ -190,9 +189,9 @@ public class TestDataNavigationElementFactory {
@NotNull
@Override
public List<Pair<String, SimpleTextAttributes>> getTitleFragments() {
Pair<String, String> relativePath = TestDataUtil.getRelativePathPairForMissingFile(myProject, myPath);
Pair<String, String> relativePath = TestDataUtil.getRelativePathPairForMissingFile(myProject, myPath.getPath());
return ContainerUtil.newSmartList(
new Pair<>(PathUtil.getFileName(myPath) + FontUtil.spaceAndThinSpace(), SimpleTextAttributes.GRAYED_ATTRIBUTES),
new Pair<>(myPath.getName() + FontUtil.spaceAndThinSpace(), SimpleTextAttributes.GRAYED_ATTRIBUTES),
new Pair<>(relativePath.first == null ? "" : relativePath.first, SimpleTextAttributes.GRAYED_BOLD_ATTRIBUTES),
new Pair<>(relativePath.first == null ? "" : "/" + relativePath.second, SimpleTextAttributes.GRAYED_ATTRIBUTES)
);
@@ -201,32 +200,37 @@ public class TestDataNavigationElementFactory {
private static class TestDataFileNavigationElement implements TestDataNavigationElement {
private final Project myProject;
private final VirtualFile myFile;
private final TestDataFile myFile;
private TestDataFileNavigationElement(@NotNull Project project, @NotNull VirtualFile file) {
private TestDataFileNavigationElement(@NotNull Project project, @NotNull TestDataFile file) {
myProject = project;
myFile = file;
}
@Override
public void performAction(@NotNull Project project) {
TestDataUtil.openOrAskToCreateFile(project, myFile.getPath());
TestDataUtil.openOrAskToCreateFile(project, myFile);
}
@Nullable
@Override
public Icon getIcon() {
return myFile.getFileType().getIcon();
VirtualFile file = myFile.getVirtualFile();
assert file != null;
return file.getFileType().getIcon();
}
@NotNull
@Override
public List<Pair<String, SimpleTextAttributes>> getTitleFragments() {
Pair<String, String> relativePath = TestDataUtil.getModuleOrProjectRelativeParentPath(myProject, myFile);
VirtualFile file = myFile.getVirtualFile();
assert file != null;
Pair<String, String> relativePath = TestDataUtil.getModuleOrProjectRelativeParentPath(myProject, myFile.getVirtualFile());
if (relativePath == null) {
// cannot calculate module/project relative path, use absolute path
return ContainerUtil.newSmartList(new Pair<>(
String.format("%s (%s)", myFile.getName(), myFile.getParent().getPath() + "/"),
String.format("%s (%s)", myFile.getName(), file.getParent().getPath() + "/"),
SimpleTextAttributes.REGULAR_ATTRIBUTES));
}
@@ -20,43 +20,37 @@ import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import static org.jetbrains.idea.devkit.testAssistant.TestDataUtil.getFileByPath;
public class TestDataNavigationHandler implements GutterIconNavigationHandler<PsiMethod> {
@Override
public void navigate(MouseEvent e, PsiMethod elt) {
List<String> fileNames = getFileNames(elt);
if (fileNames.isEmpty()) {
return;
}
List<TestDataFile> fileNames = getFileNames(elt);
if (fileNames.isEmpty()) return;
navigate(new RelativePoint(e), fileNames, elt.getProject());
}
@NotNull
static List<String> getFileNames(PsiMethod method) {
static List<TestDataFile> getFileNames(PsiMethod method) {
return getFileNames(method, true);
}
@NotNull
static List<String> getFileNames(PsiMethod method, boolean collectByExistingFiles) {
List<String> fileNames = null;
static List<TestDataFile> getFileNames(PsiMethod method, boolean collectByExistingFiles) {
List<TestDataFile> fileNames = null;
String testDataPath = TestDataLineMarkerProvider.getTestDataBasePath(method.getContainingClass());
if (testDataPath != null) {
fileNames = new TestDataReferenceCollector(testDataPath, method.getName().substring(4))
.collectTestDataReferences(method, collectByExistingFiles);
fileNames = new TestDataReferenceCollector(testDataPath, method.getName().substring(4)).collectTestDataReferences(method, collectByExistingFiles);
}
if (collectByExistingFiles && (fileNames == null || fileNames.isEmpty())) {
fileNames = new ArrayList<>();
fileNames.addAll(TestDataGuessByExistingFilesUtil.collectTestDataByExistingFiles(method));
fileNames.addAll(TestDataGuessByExistingFilesUtil.collectTestDataByExistingFiles(method, testDataPath));
fileNames.addAll(TestDataGuessByTestDiscoveryUtil.collectTestDataByExistingFiles(method));
}
return fileNames == null ? Collections.emptyList() : fileNames;
}
public static void navigate(@NotNull RelativePoint point,
@NotNull List<String> testDataFiles,
@NotNull List<TestDataFile> testDataFiles,
Project project) {
if (testDataFiles.isEmpty()) return;
if (testDataFiles.size() == 1) {
@@ -75,6 +69,7 @@ public class TestDataNavigationHandler implements GutterIconNavigationHandler<Ps
@NotNull
public static List<String> fastGetTestDataPathsByRelativePath(@NotNull String testDataFileRelativePath, PsiMethod method) {
return getFileNames(method, false).stream()
.map(TestDataFile::getPath)
.filter(path -> path.endsWith(testDataFileRelativePath.startsWith("/") ? testDataFileRelativePath : "/" + testDataFileRelativePath))
.distinct()
.collect(Collectors.toList());
@@ -86,21 +81,20 @@ public class TestDataNavigationHandler implements GutterIconNavigationHandler<Ps
* @param filePaths paths of testdata files with "/" path separator. This List can be changed.
* @param point point where the popup will be shown.
*/
private static void showNavigationPopup(Project project, List<String> filePaths, RelativePoint point) {
Collections.sort(filePaths, String.CASE_INSENSITIVE_ORDER);
private static void showNavigationPopup(Project project, List<TestDataFile> filePaths, RelativePoint point) {
Collections.sort(filePaths, Comparator.comparing(TestDataFile::getName, String.CASE_INSENSITIVE_ORDER));
List<TestDataNavigationElement> elementsToDisplay = new ArrayList<>();
List<TestDataNavigationElement> nonExistingElementsToDisplay = new ArrayList<>();
Set<VirtualFile> files = new HashSet<>();
for (String p : filePaths) {
VirtualFile f = getFileByPath(p);
if (f == null) {
Set<TestDataFile> files = new HashSet<>();
for (TestDataFile testDataFile : filePaths) {
if (!testDataFile.exists()) {
if (nonExistingElementsToDisplay.isEmpty()) {
nonExistingElementsToDisplay.add(TestDataNavigationElementFactory.createForCreateMissingFilesOption(filePaths));
}
nonExistingElementsToDisplay.add(TestDataNavigationElementFactory.createForNonExistingFile(project, p));
nonExistingElementsToDisplay.add(TestDataNavigationElementFactory.createForNonExistingFile(project, testDataFile));
} else {
files.add(f);
files.add(testDataFile);
}
}
@@ -128,39 +122,45 @@ public class TestDataNavigationHandler implements GutterIconNavigationHandler<Ps
}
private static void consumeElementsToDisplay(@NotNull Project project,
@NotNull Collection<VirtualFile> files,
@NotNull Set<TestDataFile> files,
@NotNull Consumer<TestDataNavigationElement> consumer) {
for (Map.Entry<VirtualFile, Collection<VirtualFile>> e: ContainerUtil.groupBy(files, f -> f.getParent()).entrySet()) {
Collection<VirtualFile> dirFiles = e.getValue();
Set<VirtualFile> usedPaths = new HashSet<>();
for (Map.Entry<VirtualFile, Collection<TestDataFile>> e: ContainerUtil.groupBy(files, f -> {
VirtualFile file = f.getVirtualFile();
assert file != null;
return file.getParent();
}).entrySet()) {
Collection<TestDataFile> dirFiles = e.getValue();
Set<TestDataFile> usedPaths = new HashSet<>();
for (VirtualFile file1 : dirFiles) {
if (usedPaths.contains(file1)) {
for (TestDataFile testDataFile1 : dirFiles) {
if (usedPaths.contains(testDataFile1)) {
continue;
}
boolean groupFound = false;
for (VirtualFile file2 : dirFiles) {
if (file2.equals(file1) || usedPaths.contains(file2)) {
for (TestDataFile testDataFile2 : dirFiles) {
VirtualFile file2 = testDataFile2.getVirtualFile();
assert file2 != null;
if (testDataFile1.equals(testDataFile2) || usedPaths.contains(testDataFile1)) {
continue;
}
TestDataGroupVirtualFile group = TestDataUtil.getTestDataGroup(file1, file2);
TestDataGroupVirtualFile group = TestDataUtil.getTestDataGroup(testDataFile1, testDataFile2);
if (group == null) {
continue;
}
groupFound = true;
consumer.accept(TestDataNavigationElementFactory.createForGroup(project, group));
usedPaths.add(file1);
usedPaths.add(file2);
usedPaths.add(testDataFile1);
usedPaths.add(testDataFile2);
break;
}
if (!groupFound) {
consumer.accept(TestDataNavigationElementFactory.createForFile(project, file1));
usedPaths.add(file1);
consumer.accept(TestDataNavigationElementFactory.createForFile(project, testDataFile1));
usedPaths.add(testDataFile1);
}
}
}
@@ -5,6 +5,7 @@ import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.NullableComputable;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.testFramework.PlatformTestUtil;
import org.jetbrains.annotations.NotNull;
@@ -40,14 +41,14 @@ public class TestDataReferenceCollector {
}
@NotNull
List<String> collectTestDataReferences(@NotNull final PsiMethod method) {
List<TestDataFile> collectTestDataReferences(@NotNull final PsiMethod method) {
return collectTestDataReferences(method, true);
}
@NotNull
List<String> collectTestDataReferences(@NotNull final PsiMethod method, boolean collectByExistingFiles) {
List<TestDataFile> collectTestDataReferences(@NotNull final PsiMethod method, boolean collectByExistingFiles) {
myContainingClass = method.getContainingClass();
List<String> result = collectTestDataReferences(method, new HashMap<>(), new HashSet<>());
List<TestDataFile> result = collectTestDataReferences(method, new HashMap<>(), new HashSet<>());
if (!myFoundTestDataParameters) {
myLogMessages.add("Found no parameters annotated with @TestDataFile");
}
@@ -61,10 +62,10 @@ public class TestDataReferenceCollector {
}
@NotNull
private List<String> collectTestDataReferences(final PsiMethod method,
final Map<String, Computable<UValue>> argumentMap,
final HashSet<Pair<PsiMethod, Set<UExpression>>> proceed) {
final List<String> result = new ArrayList<>();
private List<TestDataFile> collectTestDataReferences(PsiMethod method,
Map<String, Computable<UValue>> argumentMap,
HashSet<Pair<PsiMethod, Set<UExpression>>> proceed) {
final List<TestDataFile> result = new ArrayList<>();
if (myTestDataPath == null) {
return result;
}
@@ -115,7 +116,7 @@ public class TestDataReferenceCollector {
}
private void processCallArgument(UCallExpression expression, Map<String, Computable<UValue>> argumentMap,
Collection<String> result, int index) {
Collection<TestDataFile> result, int index) {
List<UExpression> arguments = expression.getValueArguments();
if (arguments.size() > index) {
handleArgument(arguments.get(index), argumentMap, result);
@@ -123,17 +124,17 @@ public class TestDataReferenceCollector {
}
private void processVarargCallArgument(UCallExpression expression, Map<String, Computable<UValue>> argumentMap,
Collection<String> result) {
Collection<TestDataFile> result) {
List<UExpression> arguments = expression.getValueArguments();
for (UExpression argument : arguments) {
handleArgument(argument, argumentMap, result);
}
}
private void handleArgument(UExpression argument, Map<String, Computable<UValue>> argumentMap, Collection<String> result) {
private void handleArgument(UExpression argument, Map<String, Computable<UValue>> argumentMap, Collection<TestDataFile> result) {
UValue testDataFileValue = UEvaluationContextKt.uValueOf(argument, new TestDataEvaluatorExtension(argumentMap));
if (testDataFileValue instanceof UStringConstant) {
result.add(myTestDataPath + ((UStringConstant) testDataFileValue).getValue());
result.add(new TestDataFile.LazyResolved(myTestDataPath + ((UStringConstant)testDataFileValue).getValue()));
}
}
});
@@ -6,17 +6,16 @@ import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.psi.PsiElement;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.util.PathUtil;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.Objects;
public class TestDataRelatedItem extends GotoRelatedItem {
private final List<String> myTestDataFiles;
private final List<TestDataFile> myTestDataFiles;
private final Editor myEditor;
public TestDataRelatedItem(@NotNull PsiElement location, @NotNull Editor editor, @NotNull List<String> testDataFiles) {
public TestDataRelatedItem(@NotNull PsiElement location, @NotNull Editor editor, @NotNull List<TestDataFile> testDataFiles) {
super(location, "Test Data");
myEditor = editor;
myTestDataFiles = testDataFiles;
@@ -26,7 +25,7 @@ public class TestDataRelatedItem extends GotoRelatedItem {
public String getCustomName() {
return myTestDataFiles.size() != 1
? "Test Data"
: PathUtil.getFileName(myTestDataFiles.get(0));
: myTestDataFiles.get(0).getName();
}
@Override
@@ -3,8 +3,6 @@ package org.jetbrains.idea.devkit.testAssistant;
import com.intellij.ide.util.PsiNavigationSupport;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.FileTypeManager;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.project.Project;
@@ -20,10 +18,10 @@ import com.intellij.util.PathUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
import org.jetbrains.idea.devkit.DevKitBundle;
import org.jetbrains.idea.devkit.testAssistant.vfs.TestDataGroupVirtualFile;
import javax.swing.*;
import java.io.File;
import java.io.IOException;
import java.util.Objects;
@@ -69,15 +67,16 @@ public class TestDataUtil {
return beforeName + " | " + afterName;
}
@TestOnly
@Nullable
static TestDataGroupVirtualFile getTestDataGroup(@NotNull String fileName1, @NotNull String fileName2) {
VirtualFile file1 = getFileByPath(fileName1);
VirtualFile file2 = getFileByPath(fileName2);
return getTestDataGroup(file1, file2);
return getTestDataGroup(new TestDataFile.LazyResolved(fileName1), new TestDataFile.LazyResolved(fileName2));
}
@Nullable
static TestDataGroupVirtualFile getTestDataGroup(@Nullable VirtualFile file1, @Nullable VirtualFile file2) {
static TestDataGroupVirtualFile getTestDataGroup(@NotNull TestDataFile testDataFile1, @NotNull TestDataFile testDataFile2) {
VirtualFile file1 = testDataFile1.getVirtualFile();
VirtualFile file2 = testDataFile2.getVirtualFile();
if (file1 == null || file2 == null) {
return null;
}
@@ -137,7 +136,7 @@ public class TestDataUtil {
!StringUtil.containsAlphaCharacters(secondNameExt.replace(TESTDATA_FILE_BEFORE_MARKER, ""));
}
static VirtualFile createFileByName(final Project project, final String path) {
static VirtualFile createFileByPath(final Project project, final String path) {
return ApplicationManager.getApplication().runWriteAction(new Computable<VirtualFile>() {
@Override
public VirtualFile compute() {
@@ -154,32 +153,22 @@ public class TestDataUtil {
});
}
static void openOrAskToCreateFile(@NotNull Project project, @NotNull String path) {
VirtualFile file = getFileByPath(path);
static void openOrAskToCreateFile(@NotNull Project project, @NotNull TestDataFile testDataFile) {
VirtualFile file = testDataFile.getVirtualFile();
if (file != null) {
PsiNavigationSupport.getInstance().createNavigatable(project, file, -1).navigate(true);
}
else {
String displayPath = getHtmlDisplayPathForMissingFile(project, path);
String displayPath = getHtmlDisplayPathForMissingFile(project, testDataFile.getPath());
int rc = Messages.showYesNoDialog(project, DevKitBundle.message("testdata.file.doesn.not.exist", displayPath),
DevKitBundle.message("testdata.create.dialog.title"), Messages.getQuestionIcon());
if (rc == Messages.YES) {
VirtualFile vFile = createFileByName(project, path);
VirtualFile vFile = createFileByPath(project, testDataFile.getPath());
PsiNavigationSupport.getInstance().createNavigatable(project, vFile, -1).navigate(true);
}
}
}
@Nullable
static Icon getIcon(@NotNull String path) {
VirtualFile file = getFileByPath(path);
if (file == null) {
return null;
}
FileType fileType = FileTypeManager.getInstance().getFileTypeByFile(file);
return fileType.getIcon();
}
@Nullable
static VirtualFile getFileByPath(String path) {
return LocalFileSystem.getInstance().refreshAndFindFileByPath(path);
@@ -25,6 +25,7 @@ import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.testFramework.PsiTestUtil;
import com.intellij.testFramework.TestDataPath;
import com.intellij.testFramework.builders.JavaModuleFixtureBuilder;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.idea.devkit.DevkitJavaTestsUtil;
import org.jetbrains.jps.model.java.JavaResourceRootType;
@@ -69,59 +70,59 @@ public class TestDataGuessByExistingFilesUtilTest extends TestDataPathTestCase {
"Test/testdata_file.txt", "TestMore/testdata_file.txt", "TestMoreRelevant/testdata_file.txt");
PsiClass testClass = (PsiClass)testMethod.getParent();
List<String> result = TestDataGuessByExistingFilesUtil.suggestTestDataFiles("testdata_file", null, testClass);
String resultPath = assertOneElement(result);
List<TestDataFile> result = TestDataGuessByExistingFilesUtil.suggestTestDataFiles("testdata_file", null, testClass);
String resultPath = assertOneElement(result).getPath();
assertTrue(resultPath, resultPath.endsWith("TestMoreRelevant/testdata_file.txt"));
}
public void testCollectTestDataByExistingFilesBeforeAndAfter() {
PsiMethod testMethod = getTestMethodWithBeforeAndAfterTestData();
List<String> result = TestDataGuessByExistingFilesUtil.collectTestDataByExistingFiles(testMethod);
List<TestDataFile> result = TestDataGuessByExistingFilesUtil.collectTestDataByExistingFiles(testMethod, null);
verifyResultForBeforeAndAfter(result);
}
public void testSuggestTestDataFilesBeforeAndAfter() {
PsiMethod testMethod = getTestMethodWithBeforeAndAfterTestData();
List<String> result = TestDataGuessByExistingFilesUtil.suggestTestDataFiles(
List<TestDataFile> result = TestDataGuessByExistingFilesUtil.suggestTestDataFiles(
TestDataGuessByExistingFilesUtil.getTestName(testMethod.getName()), null, testMethod.getContainingClass());
verifyResultForBeforeAndAfter(result);
}
public void testCollectTestDataByExistingFilesBeforeAndSame() {
PsiMethod testMethod = getTestMethodWithBeforeAndSameTestData();
List<String> result = TestDataGuessByExistingFilesUtil.collectTestDataByExistingFiles(testMethod);
List<TestDataFile> result = TestDataGuessByExistingFilesUtil.collectTestDataByExistingFiles(testMethod, null);
verifyResultForBeforeAndSame(result);
}
public void testSuggestTestDataFilesBeforeAndSame() {
PsiMethod testMethod = getTestMethodWithBeforeAndSameTestData();
List<String> result = TestDataGuessByExistingFilesUtil.suggestTestDataFiles(
List<TestDataFile> result = TestDataGuessByExistingFilesUtil.suggestTestDataFiles(
TestDataGuessByExistingFilesUtil.getTestName(testMethod.getName()), null, testMethod.getContainingClass());
verifyResultForBeforeAndSame(result);
}
public void testCollectTestDataByExistingFilesAfterAndSame() {
PsiMethod testMethod = getTestMethodWithAfterAndSameTestData();
List<String> result = TestDataGuessByExistingFilesUtil.collectTestDataByExistingFiles(testMethod);
List<TestDataFile> result = TestDataGuessByExistingFilesUtil.collectTestDataByExistingFiles(testMethod, null);
verifyResultForAfterAndSame(result);
}
public void testSuggestTestDataFilesAfterAndSame() {
PsiMethod testMethod = getTestMethodWithAfterAndSameTestData();
List<String> result = TestDataGuessByExistingFilesUtil.suggestTestDataFiles(
List<TestDataFile> result = TestDataGuessByExistingFilesUtil.suggestTestDataFiles(
TestDataGuessByExistingFilesUtil.getTestName(testMethod.getName()), null, testMethod.getContainingClass());
verifyResultForAfterAndSame(result);
}
public void testCollectTestDataByExistingFilesOnlySame() {
PsiMethod testMethod = getTestMethodWithOnlySameTestData();
List<String> result = TestDataGuessByExistingFilesUtil.collectTestDataByExistingFiles(testMethod);
List<TestDataFile> result = TestDataGuessByExistingFilesUtil.collectTestDataByExistingFiles(testMethod, null);
verifyResultForOnlySame(result);
}
public void testSuggestTestDataFilesOnlySame() {
PsiMethod testMethod = getTestMethodWithOnlySameTestData();
List<String> result = TestDataGuessByExistingFilesUtil.suggestTestDataFiles(
List<TestDataFile> result = TestDataGuessByExistingFilesUtil.suggestTestDataFiles(
TestDataGuessByExistingFilesUtil.getTestName(testMethod.getName()), null, testMethod.getContainingClass());
verifyResultForOnlySame(result);
}
@@ -154,34 +155,37 @@ public class TestDataGuessByExistingFilesUtilTest extends TestDataPathTestCase {
return testMethod;
}
private static void verifyResultForBeforeAndAfter(List<String> names) {
private static void verifyResultForBeforeAndAfter(List<TestDataFile> testDataFiles) {
List<String> names = ContainerUtil.map(testDataFiles, TestDataFile::getName);
assertNotNull(names);
assertEquals(names.toString(), 2, names.size());
Collections.sort(names);
assertTrue(names.get(0), names.get(0).endsWith("somethingBA_after.java"));
assertTrue(names.get(1), names.get(1).endsWith("somethingBA_before.java"));
assertEquals("somethingBA_after.java", names.get(0));
assertEquals("somethingBA_before.java", names.get(1));
}
private static void verifyResultForBeforeAndSame(List<String> names) {
private static void verifyResultForBeforeAndSame(List<TestDataFile> testDataFiles) {
List<String> names = ContainerUtil.map(testDataFiles, TestDataFile::getName);
assertNotNull(names);
assertEquals(names.toString(), 2, names.size());
Collections.sort(names);
assertTrue(names.get(0), names.get(0).endsWith("somethingBS.java"));
assertTrue(names.get(1), names.get(1).endsWith("somethingBS_before.java"));
assertEquals("somethingBS.java", names.get(0));
assertEquals("somethingBS_before.java", names.get(1));
}
private static void verifyResultForAfterAndSame(List<String> names) {
private static void verifyResultForAfterAndSame(List<TestDataFile> testDataFiles) {
List<String> names = ContainerUtil.map(testDataFiles, TestDataFile::getName);
assertNotNull(names);
assertEquals(names.toString(), 2, names.size());
Collections.sort(names);
assertTrue(names.get(0), names.get(0).endsWith("somethingAS.java"));
assertTrue(names.get(1), names.get(1).endsWith("somethingAS_after.java"));
assertEquals("somethingAS.java", names.get(0));
assertEquals("somethingAS_after.java", names.get(1));
}
private static void verifyResultForOnlySame(List<String> names) {
private static void verifyResultForOnlySame(List<TestDataFile> testDataFiles) {
List<String> names = ContainerUtil.map(testDataFiles, TestDataFile::getName);
assertNotNull(names);
assertEquals(names.toString(), 1, names.size());
assertTrue(names.get(0), names.get(0).endsWith("somethingS.java"));
assertSize(1, names);
assertEquals("somethingS.java", names.get(0));
}
}
@@ -20,6 +20,7 @@ import com.intellij.psi.PsiJavaFile;
import com.intellij.psi.PsiMethod;
import com.intellij.testFramework.TestDataPath;
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.idea.devkit.DevkitJavaTestsUtil;
import java.util.List;
@@ -66,7 +67,7 @@ public class TestDataReferenceCollectorTest extends LightCodeInsightFixtureTestC
for (PsiClass aClass : classes) {
if (aClass.getName().equals("ATest")) {
final PsiMethod theMethod = aClass.getMethods()[0];
return new TestDataReferenceCollector("", theMethod.getName().substring(4)).collectTestDataReferences(theMethod);
return ContainerUtil.map(new TestDataReferenceCollector("", theMethod.getName().substring(4)).collectTestDataReferences(theMethod), f -> f.getPath());
}
}
throw new RuntimeException("Couldn't find class ATest in test data file");