package com.jetbrains.lang.dart.util;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.*;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.newvfs.impl.VfsRootAccess;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.testFramework.PsiTestCase;
import com.intellij.testFramework.fixtures.CodeInsightTestFixture;
import com.intellij.util.PathUtil;
import com.intellij.util.SmartList;
import com.jetbrains.lang.dart.analyzer.DartAnalysisServerService;
import com.jetbrains.lang.dart.sdk.DartSdk;
import com.jetbrains.lang.dart.sdk.DartSdkLibUtil;
import com.jetbrains.lang.dart.sdk.DartSdkUtil;
import junit.framework.TestCase;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.TestOnly;
import org.junit.Assert;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class DartTestUtils {
public static final String BASE_TEST_DATA_PATH = findTestDataPath();
public static final String SDK_HOME_PATH = BASE_TEST_DATA_PATH + "/sdk";
private static String findTestDataPath() {
if (new File(PathManager.getHomePath() + "/contrib").isDirectory()) {
// started from IntelliJ IDEA Ultimate project
return FileUtil.toSystemIndependentName(PathManager.getHomePath() + "/contrib/Dart/testData");
}
final File f = new File("testData");
if (f.isDirectory()) {
// started from 'Dart-plugin' project
return FileUtil.toSystemIndependentName(f.getAbsolutePath());
}
final String parentPath = PathUtil.getParentPath(PathManager.getHomePath());
if (new File(parentPath + "/intellij-plugins").isDirectory()) {
// started from IntelliJ IDEA Community Edition + Dart Plugin project
return FileUtil.toSystemIndependentName(parentPath + "/intellij-plugins/Dart/testData");
}
if (new File(parentPath + "/contrib").isDirectory()) {
// started from IntelliJ IDEA Community + Dart Plugin project
return FileUtil.toSystemIndependentName(parentPath + "/contrib/Dart/testData");
}
return "";
}
@TestOnly
public static void configureDartSdk(@NotNull final Module module, @NotNull final Disposable disposable, final boolean realSdk) {
final String sdkHome;
if (realSdk) {
sdkHome = System.getProperty("dart.sdk");
if (sdkHome == null) {
Assert.fail("To run tests that use Dart Analysis Server you need to add '-Ddart.sdk=[real SDK home]' to the VM Options field of " +
"the corresponding JUnit run configuration (Run | Edit Configurations)");
}
if (!DartSdkUtil.isDartSdkHome(sdkHome)) {
Assert.fail("Incorrect path to the Dart SDK (" + sdkHome + ") is set as '-Ddart.sdk' VM option of " +
"the corresponding JUnit run configuration (Run | Edit Configurations)");
}
}
else {
sdkHome = SDK_HOME_PATH;
}
VfsRootAccess.allowRootAccess(disposable, sdkHome);
ApplicationManager.getApplication().runWriteAction(() -> {
DartSdkLibUtil.ensureDartSdkConfigured(module.getProject(), sdkHome);
DartSdkLibUtil.enableDartSdk(module);
});
}
public static List<CaretPositionInfo> extractPositionMarkers(@NotNull final Project project, @NotNull final Document document) {
final Pattern caretPattern = Pattern.compile(
"<caret(?: expected=\'([^\']*)\')?(?: completionEquals=\'([^\']*)\')?(?: completionIncludes=\'([^\']*)\')?(?: completionExcludes=\'([^\']*)\')?>");
final List<CaretPositionInfo> result = new ArrayList<>();
WriteCommandAction.runWriteCommandAction(null, (Runnable)() -> {
while (true) {
Matcher m = caretPattern.matcher(document.getImmutableCharSequence());
if (m.find()) {
document.deleteString(m.start(), m.end());
final int caretOffset = m.start();
final String expected = m.group(1);
final String completionEqualsRaw = m.group(2);
final List<String> completionEqualsList = completionEqualsRaw == null ? null : StringUtil.split(completionEqualsRaw, ",");
final String completionIncludesRaw = m.group(3);
final List<String> completionIncludesList = completionIncludesRaw == null ? null : StringUtil.split(completionIncludesRaw, ",");
final String completionExcludesRaw = m.group(4);
final List<String> completionExcludesList = completionExcludesRaw == null ? null : StringUtil.split(completionExcludesRaw, ",");
result.add(new CaretPositionInfo(caretOffset, expected, completionEqualsList, completionIncludesList, completionExcludesList));
}
else {
break;
}
}
});
if (!result.isEmpty()) {
PsiDocumentManager.getInstance(project).commitDocument(document);
}
return result;
}
/**
* Use this method in finally{} clause if the test modifies excluded roots or configures module libraries
*/
public static void resetModuleRoots(@NotNull final Module module) {
ApplicationManager.getApplication().runWriteAction(() -> {
final ModifiableRootModel modifiableModel = ModuleRootManager.getInstance(module).getModifiableModel();
try {
final List<OrderEntry> entriesToRemove = new SmartList<>();
for (OrderEntry orderEntry : modifiableModel.getOrderEntries()) {
if (orderEntry instanceof LibraryOrderEntry) {
entriesToRemove.add(orderEntry);
}
}
for (OrderEntry orderEntry : entriesToRemove) {
modifiableModel.removeOrderEntry(orderEntry);
}
final ContentEntry[] contentEntries = modifiableModel.getContentEntries();
TestCase.assertEquals("Expected one content root, got: " + contentEntries.length, 1, contentEntries.length);
final ContentEntry oldContentEntry = contentEntries[0];
if (oldContentEntry.getSourceFolders().length != 1 || oldContentEntry.getExcludeFolderUrls().size() > 0) {
modifiableModel.removeContentEntry(oldContentEntry);
final ContentEntry newContentEntry = modifiableModel.addContentEntry(oldContentEntry.getUrl());
newContentEntry.addSourceFolder(newContentEntry.getUrl(), false);
}
if (modifiableModel.isChanged()) {
modifiableModel.commit();
}
}
finally {
if (!modifiableModel.isDisposed()) {
modifiableModel.dispose();
}
}
});
}
public static VirtualFile configureNavigation(@NotNull PsiTestCase test,
@NotNull VirtualFile testRoot,
@NotNull final VirtualFile... vFiles) {
DartAnalysisServerService.getInstance(test.getProject()).serverReadyForRequest(test.getProject());
// Trigger navigation requests for each file that needs to have navigation data during resolution.
for (VirtualFile vFile : vFiles) {
String name = vFile.getName();
VirtualFile testFile = testRoot.findChild(name);
if (testFile == null) TestCase.fail();
DartAnalysisServerService.getInstance(test.getProject()).analysis_getNavigation(testFile, 0, (int)testFile.getLength());
}
return testRoot;
}
public static void letAnalyzerSmellCoreFile(@NotNull final CodeInsightTestFixture fixture, @NotNull final String fileName)
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
final DartSdk sdk = DartSdk.getDartSdk(fixture.getProject());
final VirtualFile iterableFile = LocalFileSystem.getInstance().findFileByPath(sdk.getHomePath() + "/lib/core/" + fileName);
TestCase.assertNotNull(iterableFile);
fixture.openFileInEditor(iterableFile);
// let's keep updateVisibleFiles() method package-local, but here we need to invoke it because FileEditorManagerListener is not notified in test environment
final Method method = DartAnalysisServerService.class.getDeclaredMethod("updateVisibleFiles");
method.setAccessible(true);
method.invoke(DartAnalysisServerService.getInstance(fixture.getProject()));
}
}