193: Fix tests compilation for 193 platform

This commit is contained in:
Vyacheslav Gerasimov
2019-09-21 15:36:13 +04:00
parent a8c72b7e84
commit d54bc3a4bb
21 changed files with 4026 additions and 0 deletions
@@ -0,0 +1,332 @@
/*
* Copyright 2000-2016 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 org.jetbrains.kotlin.test.testFramework;
import com.intellij.core.CoreASTFactory;
import com.intellij.lang.*;
import com.intellij.lang.impl.PsiBuilderFactoryImpl;
import com.intellij.mock.*;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.EditorFactory;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileEditor.impl.LoadTextUtil;
import com.intellij.openapi.fileTypes.FileTypeFactory;
import com.intellij.openapi.fileTypes.FileTypeManager;
import com.intellij.openapi.progress.EmptyProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.impl.CoreProgressManager;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.CharsetToolkit;
import com.intellij.pom.PomModel;
import com.intellij.pom.core.impl.PomModelImpl;
import com.intellij.pom.tree.TreeAspect;
import com.intellij.psi.*;
import com.intellij.psi.impl.*;
import com.intellij.psi.util.CachedValuesManager;
import com.intellij.testFramework.LightVirtualFile;
import com.intellij.testFramework.TestDataFile;
import com.intellij.util.CachedValuesManagerImpl;
import com.intellij.util.Function;
import com.intellij.util.messages.MessageBus;
import com.intellij.util.messages.MessageBusFactory;
import junit.framework.TestCase;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.picocontainer.ComponentAdapter;
import org.picocontainer.MutablePicoContainer;
import java.io.File;
import java.io.IOException;
import java.util.Set;
@SuppressWarnings("ALL")
public abstract class KtParsingTestCase extends KtPlatformLiteFixture {
public static final Key<Document> HARD_REF_TO_DOCUMENT_KEY = Key.create("HARD_REF_TO_DOCUMENT_KEY");
protected String myFilePrefix = "";
protected String myFileExt;
protected final String myFullDataPath;
protected PsiFile myFile;
private MockPsiManager myPsiManager;
private PsiFileFactoryImpl myFileFactory;
protected Language myLanguage;
private final ParserDefinition[] myDefinitions;
private final boolean myLowercaseFirstLetter;
protected KtParsingTestCase(@NonNls @NotNull String dataPath, @NotNull String fileExt, @NotNull ParserDefinition... definitions) {
this(dataPath, fileExt, false, definitions);
}
protected KtParsingTestCase(@NonNls @NotNull String dataPath, @NotNull String fileExt, boolean lowercaseFirstLetter, @NotNull ParserDefinition... definitions) {
myDefinitions = definitions;
myFullDataPath = getTestDataPath() + "/" + dataPath;
myFileExt = fileExt;
myLowercaseFirstLetter = lowercaseFirstLetter;
}
@Override
protected void setUp() throws Exception {
super.setUp();
initApplication();
ComponentAdapter component = getApplication().getPicoContainer().getComponentAdapter(ProgressManager.class.getName());
Extensions.registerAreaClass("IDEA_PROJECT", null);
myProject = new MockProjectEx(getTestRootDisposable());
myPsiManager = new MockPsiManager(myProject);
myFileFactory = new PsiFileFactoryImpl(myPsiManager);
MutablePicoContainer appContainer = getApplication().getPicoContainer();
registerComponentInstance(appContainer, MessageBus.class, MessageBusFactory.newMessageBus(getApplication()));
final MockEditorFactory editorFactory = new MockEditorFactory();
registerComponentInstance(appContainer, EditorFactory.class, editorFactory);
registerComponentInstance(appContainer, FileDocumentManager.class, new MockFileDocumentManagerImpl(new Function<CharSequence, Document>() {
@Override
public Document fun(CharSequence charSequence) {
return editorFactory.createDocument(charSequence);
}
}, HARD_REF_TO_DOCUMENT_KEY));
registerComponentInstance(appContainer, PsiDocumentManager.class, new MockPsiDocumentManager());
registerApplicationService(PsiBuilderFactory.class, new PsiBuilderFactoryImpl());
registerApplicationService(DefaultASTFactory.class, new CoreASTFactory());
registerApplicationService(ProgressManager.class, new CoreProgressManager());
myProject.registerService(CachedValuesManager.class, new CachedValuesManagerImpl(myProject, new PsiCachedValuesFactory(myPsiManager)));
myProject.registerService(PsiManager.class, myPsiManager);
this.registerExtensionPoint(FileTypeFactory.FILE_TYPE_FACTORY_EP, FileTypeFactory.class);
registerExtensionPoint(MetaLanguage.EP_NAME, MetaLanguage.class);
for (ParserDefinition definition : myDefinitions) {
addExplicitExtension(LanguageParserDefinitions.INSTANCE, definition.getFileNodeType().getLanguage(), definition);
}
if (myDefinitions.length > 0) {
configureFromParserDefinition(myDefinitions[0], myFileExt);
}
// That's for reparse routines
final PomModelImpl pomModel = new PomModelImpl(myProject);
myProject.registerService(PomModel.class, pomModel);
new TreeAspect(pomModel);
}
public void configureFromParserDefinition(ParserDefinition definition, String extension) {
myLanguage = definition.getFileNodeType().getLanguage();
myFileExt = extension;
addExplicitExtension(LanguageParserDefinitions.INSTANCE, this.myLanguage, definition);
registerComponentInstance(
getApplication().getPicoContainer(), FileTypeManager.class,
new MockFileTypeManager(new MockLanguageFileType(myLanguage, myFileExt)));
}
protected <T> void addExplicitExtension(final LanguageExtension<T> instance, final Language language, final T object) {
instance.addExplicitExtension(language, object);
Disposer.register(myProject, new Disposable() {
@Override
public void dispose() {
instance.removeExplicitExtension(language, object);
}
});
}
@Override
protected <T> void registerExtensionPoint(final ExtensionPointName<T> extensionPointName, Class<T> aClass) {
super.registerExtensionPoint(extensionPointName, aClass);
Disposer.register(myProject, new Disposable() {
@Override
public void dispose() {
Extensions.getRootArea().unregisterExtensionPoint(extensionPointName.getName());
}
});
}
protected <T> void registerApplicationService(final Class<T> aClass, T object) {
getApplication().registerService(aClass, object);
Disposer.register(myProject, new Disposable() {
@Override
public void dispose() {
getApplication().getPicoContainer().unregisterComponent(aClass.getName());
}
});
}
public MockProjectEx getProject() {
return myProject;
}
public MockPsiManager getPsiManager() {
return myPsiManager;
}
@Override
protected void tearDown() throws Exception {
super.tearDown();
myFile = null;
myProject = null;
myPsiManager = null;
}
protected String getTestDataPath() {
return PathManager.getHomePath();
}
@NotNull
public final String getTestName() {
return getTestName(myLowercaseFirstLetter);
}
protected boolean includeRanges() {
return false;
}
protected boolean skipSpaces() {
return false;
}
protected boolean checkAllPsiRoots() {
return true;
}
protected void doTest(boolean checkResult) {
String name = getTestName();
try {
String text = loadFile(name + "." + myFileExt);
myFile = createPsiFile(name, text);
ensureParsed(myFile);
assertEquals("light virtual file text mismatch", text, ((LightVirtualFile)myFile.getVirtualFile()).getContent().toString());
assertEquals("virtual file text mismatch", text, LoadTextUtil.loadText(myFile.getVirtualFile()));
assertEquals("doc text mismatch", text, myFile.getViewProvider().getDocument().getText());
assertEquals("psi text mismatch", text, myFile.getText());
ensureCorrectReparse(myFile);
if (checkResult){
checkResult(name, myFile);
}
else{
toParseTreeText(myFile, skipSpaces(), includeRanges());
}
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
protected void doTest(String suffix) throws IOException {
String name = getTestName();
String text = loadFile(name + "." + myFileExt);
myFile = createPsiFile(name, text);
ensureParsed(myFile);
assertEquals(text, myFile.getText());
checkResult(name + suffix, myFile);
}
protected void doCodeTest(String code) throws IOException {
String name = getTestName();
myFile = createPsiFile("a", code);
ensureParsed(myFile);
assertEquals(code, myFile.getText());
checkResult(myFilePrefix + name, myFile);
}
protected PsiFile createPsiFile(String name, String text) {
return createFile(name + "." + myFileExt, text);
}
protected PsiFile createFile(@NonNls String name, String text) {
LightVirtualFile virtualFile = new LightVirtualFile(name, myLanguage, text);
virtualFile.setCharset(CharsetToolkit.UTF8_CHARSET);
return createFile(virtualFile);
}
protected PsiFile createFile(LightVirtualFile virtualFile) {
return myFileFactory.trySetupPsiForFile(virtualFile, myLanguage, true, false);
}
protected void checkResult(@NonNls @TestDataFile String targetDataName, final PsiFile file) throws IOException {
doCheckResult(myFullDataPath, file, checkAllPsiRoots(), targetDataName, skipSpaces(), includeRanges());
}
public static void doCheckResult(String testDataDir,
PsiFile file,
boolean checkAllPsiRoots,
String targetDataName,
boolean skipSpaces,
boolean printRanges) throws IOException {
FileViewProvider provider = file.getViewProvider();
Set<Language> languages = provider.getLanguages();
if (!checkAllPsiRoots || languages.size() == 1) {
doCheckResult(testDataDir, targetDataName + ".txt", toParseTreeText(file, skipSpaces, printRanges).trim());
return;
}
for (Language language : languages) {
PsiFile root = provider.getPsi(language);
String expectedName = targetDataName + "." + language.getID() + ".txt";
doCheckResult(testDataDir, expectedName, toParseTreeText(root, skipSpaces, printRanges).trim());
}
}
protected void checkResult(String actual) throws IOException {
String name = getTestName();
doCheckResult(myFullDataPath, myFilePrefix + name + ".txt", actual);
}
protected void checkResult(@TestDataFile @NonNls String targetDataName, String actual) throws IOException {
doCheckResult(myFullDataPath, targetDataName, actual);
}
public static void doCheckResult(String fullPath, String targetDataName, String actual) throws IOException {
String expectedFileName = fullPath + File.separatorChar + targetDataName;
KtUsefulTestCase.assertSameLinesWithFile(expectedFileName, actual);
}
protected static String toParseTreeText(PsiElement file, boolean skipSpaces, boolean printRanges) {
return DebugUtil.psiToString(file, skipSpaces, printRanges);
}
protected String loadFile(@NonNls @TestDataFile String name) throws IOException {
return loadFileDefault(myFullDataPath, name);
}
public static String loadFileDefault(String dir, String name) throws IOException {
return FileUtil.loadFile(new File(dir, name), CharsetToolkit.UTF8, true).trim();
}
public static void ensureParsed(PsiFile file) {
file.accept(new PsiElementVisitor() {
@Override
public void visitElement(PsiElement element) {
element.acceptChildren(this);
}
});
}
public static void ensureCorrectReparse(@NotNull PsiFile file) {
String psiToStringDefault = DebugUtil.psiToString(file, false, false);
String fileText = file.getText();
DiffLog diffLog = (new BlockSupportImpl()).reparseRange(
file, file.getNode(), TextRange.allOf(fileText), fileText, new EmptyProgressIndicator(), fileText);
diffLog.performActualPsiChange(file);
TestCase.assertEquals(psiToStringDefault, DebugUtil.psiToString(file, false, false));
}
}
@@ -0,0 +1,81 @@
/*
* Copyright 2000-2014 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 org.jetbrains.kotlin.test.testFramework;
import com.intellij.core.CoreEncodingProjectManager;
import com.intellij.mock.MockApplicationEx;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.extensions.ExtensionPoint;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.extensions.ExtensionsArea;
import com.intellij.openapi.fileTypes.FileTypeManager;
import com.intellij.openapi.vfs.encoding.EncodingManager;
import org.picocontainer.MutablePicoContainer;
import java.lang.reflect.Modifier;
public abstract class KtPlatformLiteFixture extends KtUsefulTestCase {
protected MockProjectEx myProject;
@Override
protected void setUp() throws Exception {
super.setUp();
// FIXME: There is no `Extensions.cleanRootArea` in 193
// Extensions.cleanRootArea(getTestRootDisposable());
}
public static MockApplicationEx getApplication() {
return (MockApplicationEx)ApplicationManager.getApplication();
}
public void initApplication() {
MockApplicationEx instance = new MockApplicationEx(getTestRootDisposable());
ApplicationManager.setApplication(instance, FileTypeManager::getInstance, getTestRootDisposable());
getApplication().registerService(EncodingManager.class, CoreEncodingProjectManager.class);
}
@Override
protected void tearDown() throws Exception {
super.tearDown();
clearFields(this);
myProject = null;
}
protected <T> void registerExtensionPoint(ExtensionPointName<T> extensionPointName, Class<T> aClass) {
registerExtensionPoint(Extensions.getRootArea(), extensionPointName, aClass);
}
private static <T> void registerExtensionPoint(
ExtensionsArea area, ExtensionPointName<T> extensionPointName,
Class<? extends T> aClass
) {
String name = extensionPointName.getName();
if (!area.hasExtensionPoint(name)) {
ExtensionPoint.Kind kind = aClass.isInterface() || (aClass.getModifiers() & Modifier.ABSTRACT) != 0 ? ExtensionPoint.Kind.INTERFACE : ExtensionPoint.Kind.BEAN_CLASS;
area.registerExtensionPoint(name, aClass.getName(), kind);
}
}
@SuppressWarnings("unchecked")
public static <T> T registerComponentInstance(MutablePicoContainer container, Class<T> key, T implementation) {
Object old = container.getComponentInstance(key);
container.unregisterComponent(key);
container.registerComponentInstance(key, implementation);
return (T)old;
}
}
@@ -0,0 +1,179 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.completion.test.confidence;
import com.intellij.codeInsight.CodeInsightSettings;
import com.intellij.codeInsight.completion.CodeCompletionHandlerBase;
import com.intellij.codeInsight.completion.CompletionType;
import com.intellij.codeInsight.completion.LightCompletionTestCase;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupManager;
import com.intellij.codeInsight.lookup.impl.LookupImpl;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.projectRoots.JavaSdk;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiFile;
import com.intellij.util.ArrayUtil;
import org.apache.commons.lang.SystemUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.idea.completion.test.CompletionTestUtilKt;
import org.jetbrains.kotlin.idea.test.TestUtilsKt;
import org.jetbrains.kotlin.test.InTextDirectivesUtils;
import org.jetbrains.kotlin.test.JUnit3WithIdeaConfigurationRunner;
import org.junit.runner.RunWith;
import java.io.File;
import java.lang.reflect.Method;
import java.util.List;
@RunWith(JUnit3WithIdeaConfigurationRunner.class)
public class KotlinConfidenceTest extends LightCompletionTestCase {
private static final String TYPE_DIRECTIVE_PREFIX = "// TYPE:";
private final ThreadLocal<Boolean> skipComplete = ThreadLocal.withInitial(() -> false);
public void testCompleteOnDotOutOfRanges() {
doTest();
}
public void testImportAsConfidence() {
doTest();
}
public void testInBlockOfFunctionLiteral() {
doTest();
}
public void testInModifierList() {
doTest();
}
public void testNoAutoCompletionForRangeOperator() {
doTest();
}
public void testNoAutoPopupInString() {
doTest();
}
public void testAutoPopupInStringTemplate() {
doTest();
}
public void testAutoPopupInStringTemplateAfterDollar() {
doTest();
}
public void testNoAutoPopupInStringTemplateAfterSpace() {
doTest();
}
public void testNoAutoPopupInRawStringTemplateAfterNewLine() {
doTest();
}
@Override
protected void setUp() throws Exception {
super.setUp();
TestUtilsKt.invalidateLibraryCache(getProject());
}
protected void doTest() {
boolean completeByChars = CodeInsightSettings.getInstance().SELECT_AUTOPOPUP_SUGGESTIONS_BY_CHARS;
CodeInsightSettings.getInstance().SELECT_AUTOPOPUP_SUGGESTIONS_BY_CHARS = true;
try {
skipComplete.set(true);
try {
configureByFile(getBeforeFileName());
} finally {
skipComplete.set(false);
}
String text = getEditor().getDocument().getText();
boolean noLookup = InTextDirectivesUtils.isDirectiveDefined(text, "// NO_LOOKUP");
if (!noLookup) complete(); //This will cause NPE in case of absence of autopopup completion
List<String> expectedElements = InTextDirectivesUtils.findLinesWithPrefixesRemoved(text, "// ELEMENT:");
assertFalse("Can't both expect lookup elements and no lookup", !expectedElements.isEmpty() && noLookup);
if (noLookup) {
try {
Method m = null; //Looking for shouldSkipAutoPopup method (note, we can't use name because of scrambling in Ultimate)
for (Method method : CodeCompletionHandlerBase.class.getDeclaredMethods()) {
Class<?>[] parameterTypes = method.getParameterTypes();
if (method.getReturnType().equals(Boolean.TYPE) &&
parameterTypes.length == 2 &&
parameterTypes[0].equals(Editor.class) &&
parameterTypes[1].equals(PsiFile.class)) {
assertNull("Only one method with such signature should exist", m);
m = method;
}
}
assertNotNull(m);
m.setAccessible(true);
Object o = m.invoke(null, getEditor(), getFile());
assert o instanceof Boolean;
assertTrue("Should skip autopopup completion", ((Boolean) o).booleanValue());
} catch (Throwable e) {
throw new AssertionError(e);
}
return;
}
String typeText = getTypeText(text);
if (!expectedElements.isEmpty()) {
assertContainsItems(ArrayUtil.toStringArray(expectedElements));
return;
}
assertNotNull("You must type something, use // TYPE:", typeText);
type(typeText);
checkResultByFile(getAfterFileName());
}
finally {
CodeInsightSettings.getInstance().SELECT_AUTOPOPUP_SUGGESTIONS_BY_CHARS = completeByChars;
}
}
private static String getTypeText(String text) {
String[] directives = InTextDirectivesUtils.findArrayWithPrefixes(text, TYPE_DIRECTIVE_PREFIX);
if (directives.length == 0) return null;
assertEquals("One directive with \"" + TYPE_DIRECTIVE_PREFIX +"\" expected", 1, directives.length);
return StringUtil.unquoteString(directives[0]);
}
private String getBeforeFileName() {
return getTestName(false) + ".kt";
}
private String getAfterFileName() {
return getTestName(false) + ".kt.after";
}
@NotNull
@Override
protected String getTestDataPath() {
return new File(CompletionTestUtilKt.getCOMPLETION_TEST_DATA_BASE_PATH(), "/confidence/").getPath() + File.separator;
}
@Override
protected Sdk getProjectJDK() {
return JavaSdk.getInstance().createJdk("JDK", SystemUtils.getJavaHome().getAbsolutePath());
}
@Override
protected void complete() {
if (skipComplete.get()) return;
new CodeCompletionHandlerBase(CompletionType.BASIC, false, true, true).invokeCompletion(
getProject(), getEditor(), 0, false);
LookupImpl lookup = (LookupImpl) LookupManager.getActiveLookup(getEditor());
myItems = lookup == null ? null : lookup.getItems().toArray(LookupElement.EMPTY_ARRAY);
myPrefix = lookup == null ? null : lookup.itemPattern(lookup.getItems().get(0));
}
}
@@ -0,0 +1,244 @@
/*
* Copyright 2010-2016 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 org.jetbrains.kotlin.idea.codeInsight.gradle;
import com.google.common.collect.Multimap;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.io.StreamUtil;
import com.intellij.testFramework.IdeaTestUtil;
import com.intellij.util.containers.ContainerUtil;
import org.codehaus.groovy.runtime.typehandling.ShortTypeHandling;
import org.gradle.tooling.BuildActionExecuter;
import org.gradle.tooling.GradleConnector;
import org.gradle.tooling.ProjectConnection;
import org.gradle.tooling.internal.consumer.DefaultGradleConnector;
import org.gradle.util.GradleVersion;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.gradle.model.ClassSetProjectImportModelProvider;
import org.jetbrains.plugins.gradle.model.ExternalProject;
import org.jetbrains.plugins.gradle.model.ProjectImportAction;
import org.jetbrains.plugins.gradle.service.execution.GradleExecutionHelper;
import org.jetbrains.plugins.gradle.tooling.builder.ModelBuildScriptClasspathBuilderImpl;
import org.jetbrains.plugins.gradle.util.GradleConstants;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.rules.TestName;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assume.assumeThat;
// part of org.jetbrains.plugins.gradle.tooling.builder.AbstractModelBuilderTest
@RunWith(value = Parameterized.class)
public abstract class AbstractModelBuilderTest {
public static final Object[][] SUPPORTED_GRADLE_VERSIONS = {{"3.5"}, {"4.9"}};
private static final Pattern TEST_METHOD_NAME_PATTERN = Pattern.compile("(.*)\\[(\\d*: with Gradle-.*)\\]");
private static File ourTempDir;
@NotNull
private final String gradleVersion;
private File testDir;
private ProjectImportAction.AllModels allModels;
@Rule public TestName name = new TestName();
@Rule public VersionMatcherRule versionMatcherRule = new VersionMatcherRule();
public AbstractModelBuilderTest(@NotNull String gradleVersion) {
this.gradleVersion = gradleVersion;
}
@Parameterized.Parameters(name = "{index}: with Gradle-{0}")
public static Collection<Object[]> data() {
return Arrays.asList(SUPPORTED_GRADLE_VERSIONS);
}
@Before
public void setUp() throws Exception {
assumeThat(gradleVersion, versionMatcherRule.getMatcher());
ensureTempDirCreated();
String methodName = name.getMethodName();
Matcher m = TEST_METHOD_NAME_PATTERN.matcher(methodName);
if (m.matches()) {
methodName = m.group(1);
}
testDir = new File(ourTempDir, methodName);
FileUtil.ensureExists(testDir);
InputStream buildScriptStream = getClass().getResourceAsStream("/" + methodName + "/" + GradleConstants.DEFAULT_SCRIPT_NAME);
try {
FileUtil.writeToFile(
new File(testDir, GradleConstants.DEFAULT_SCRIPT_NAME),
FileUtil.loadTextAndClose(buildScriptStream)
);
}
finally {
StreamUtil.closeStream(buildScriptStream);
}
InputStream settingsStream = getClass().getResourceAsStream("/" + methodName + "/" + GradleConstants.SETTINGS_FILE_NAME);
try {
if (settingsStream != null) {
FileUtil.writeToFile(
new File(testDir, GradleConstants.SETTINGS_FILE_NAME),
FileUtil.loadTextAndClose(settingsStream)
);
}
}
finally {
StreamUtil.closeStream(settingsStream);
}
GradleConnector connector = GradleConnector.newConnector();
URI distributionUri = new DistributionLocator().getDistributionFor(GradleVersion.version(gradleVersion));
connector.useDistribution(distributionUri);
connector.forProjectDirectory(testDir);
int daemonMaxIdleTime = 10;
try {
daemonMaxIdleTime = Integer.parseInt(System.getProperty("gradleDaemonMaxIdleTime", "10"));
}
catch (NumberFormatException ignore) {
}
((DefaultGradleConnector) connector).daemonMaxIdleTime(daemonMaxIdleTime, TimeUnit.SECONDS);
ProjectConnection connection = connector.connect();
try {
ProjectImportAction projectImportAction = new ProjectImportAction(false);
projectImportAction.addProjectImportModelProvider(new ClassSetProjectImportModelProvider(getModels()));
BuildActionExecuter<ProjectImportAction.AllModels> buildActionExecutor = connection.action(projectImportAction);
File initScript = GradleExecutionHelper.generateInitScript(false, getToolingExtensionClasses());
assertNotNull(initScript);
String jdkHome = IdeaTestUtil.requireRealJdkHome();
buildActionExecutor.setJavaHome(new File(jdkHome));
buildActionExecutor.setJvmArguments("-Xmx128m", "-XX:MaxPermSize=64m");
buildActionExecutor
.withArguments("--info", "--recompile-scripts", GradleConstants.INIT_SCRIPT_CMD_OPTION, initScript.getAbsolutePath());
allModels = buildActionExecutor.run();
assertNotNull(allModels);
}
finally {
connection.close();
}
}
@NotNull
private static Set<Class> getToolingExtensionClasses() {
Set<Class> classes = ContainerUtil.<Class>set(
ExternalProject.class,
// gradle-tooling-extension-api jar
ProjectImportAction.class,
// gradle-tooling-extension-impl jar
ModelBuildScriptClasspathBuilderImpl.class,
Multimap.class,
ShortTypeHandling.class
);
ContainerUtil.addAllNotNull(classes, doGetToolingExtensionClasses());
return classes;
}
@NotNull
private static Set<Class> doGetToolingExtensionClasses() {
return Collections.emptySet();
}
@After
public void tearDown() throws Exception {
if (testDir != null) {
FileUtil.delete(testDir);
}
}
protected abstract Set<Class> getModels();
private static void ensureTempDirCreated() throws IOException {
if (ourTempDir != null) return;
ourTempDir = new File(FileUtil.getTempDirectory(), "gradleTests");
FileUtil.delete(ourTempDir);
FileUtil.ensureExists(ourTempDir);
}
public static class DistributionLocator {
private static final String RELEASE_REPOSITORY_ENV = "GRADLE_RELEASE_REPOSITORY";
private static final String SNAPSHOT_REPOSITORY_ENV = "GRADLE_SNAPSHOT_REPOSITORY";
private static final String GRADLE_RELEASE_REPO = "https://services.gradle.org/distributions";
private static final String GRADLE_SNAPSHOT_REPO = "https://services.gradle.org/distributions-snapshots";
@NotNull private final String myReleaseRepoUrl;
@NotNull private final String mySnapshotRepoUrl;
public DistributionLocator() {
this(DistributionLocator.getRepoUrl(false), DistributionLocator.getRepoUrl(true));
}
public DistributionLocator(@NotNull String releaseRepoUrl, @NotNull String snapshotRepoUrl) {
myReleaseRepoUrl = releaseRepoUrl;
mySnapshotRepoUrl = snapshotRepoUrl;
}
@NotNull
public URI getDistributionFor(@NotNull GradleVersion version) throws URISyntaxException {
return getDistribution(getDistributionRepository(version), version, "gradle", "bin");
}
@NotNull
private String getDistributionRepository(@NotNull GradleVersion version) {
return version.isSnapshot() ? mySnapshotRepoUrl : myReleaseRepoUrl;
}
private static URI getDistribution(
@NotNull String repositoryUrl,
@NotNull GradleVersion version,
@NotNull String archiveName,
@NotNull String archiveClassifier
) throws URISyntaxException {
return new URI(String.format("%s/%s-%s-%s.zip", repositoryUrl, archiveName, version.getVersion(), archiveClassifier));
}
@NotNull
public static String getRepoUrl(boolean isSnapshotUrl) {
String envRepoUrl = System.getenv(isSnapshotUrl ? SNAPSHOT_REPOSITORY_ENV : RELEASE_REPOSITORY_ENV);
if (envRepoUrl != null) return envRepoUrl;
return isSnapshotUrl ? GRADLE_SNAPSHOT_REPO : GRADLE_RELEASE_REPO;
}
}
}
@@ -0,0 +1,551 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.perf
import com.intellij.codeInsight.daemon.*
import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl
import com.intellij.codeInsight.daemon.impl.HighlightInfo
import com.intellij.codeInsight.daemon.impl.IdentifierHighlighterPassFactory
import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.ide.highlighter.ModuleFileType
import com.intellij.ide.startup.impl.StartupManagerImpl
import com.intellij.idea.IdeaTestApplication
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.runWriteAction
import com.intellij.openapi.editor.EditorFactory
import com.intellij.openapi.externalSystem.service.project.manage.ExternalProjectsManagerImpl
import com.intellij.openapi.fileEditor.FileDocumentManager
import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.fileEditor.impl.FileEditorManagerImpl
import com.intellij.openapi.module.ModuleManager
import com.intellij.openapi.module.ModuleTypeId
import com.intellij.openapi.project.DumbService
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.ex.ProjectManagerEx
import com.intellij.openapi.project.impl.ProjectImpl
import com.intellij.openapi.projectRoots.JavaSdk
import com.intellij.openapi.projectRoots.ProjectJdkTable
import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl
import com.intellij.openapi.roots.ProjectRootManager
import com.intellij.openapi.roots.ex.ProjectRootManagerEx
import com.intellij.openapi.startup.StartupManager
import com.intellij.openapi.vcs.changes.ChangeListManager
import com.intellij.openapi.vcs.changes.ChangeListManagerImpl
import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiManager
import com.intellij.testFramework.*
import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl
import com.intellij.util.ArrayUtilRt
import com.intellij.util.ThrowableRunnable
import com.intellij.util.indexing.UnindexedFilesUpdater
import com.intellij.util.io.exists
import org.jetbrains.kotlin.idea.configuration.getModulesWithKotlinFiles
import org.jetbrains.kotlin.idea.core.script.ScriptConfigurationManager
import org.jetbrains.kotlin.idea.framework.KotlinSdkType
import org.jetbrains.kotlin.idea.perf.Stats.Companion.WARM_UP
import org.jetbrains.kotlin.idea.perf.Stats.Companion.runAndMeasure
import org.jetbrains.kotlin.idea.project.getAndCacheLanguageLevelByDependencies
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil
import org.jetbrains.kotlin.idea.test.invalidateLibraryCache
import org.jetbrains.kotlin.idea.testFramework.*
import org.jetbrains.kotlin.idea.testFramework.Fixture.Companion.cleanupCaches
import org.jetbrains.kotlin.idea.testFramework.Fixture.Companion.isAKotlinScriptFile
import org.jetbrains.kotlin.idea.testFramework.Fixture.Companion.openFileInEditor
import org.jetbrains.kotlin.idea.testFramework.Fixture.Companion.openFixture
import java.io.File
import java.nio.file.Paths
abstract class AbstractPerformanceProjectsTest : UsefulTestCase() {
// myProject is not required for all potential perf test cases
protected var myProject: Project? = null
private lateinit var jdk18: Sdk
private lateinit var myApplication: IdeaTestApplication
override fun isStressTest(): Boolean = true
override fun isPerformanceTest(): Boolean = false
override fun setUp() {
super.setUp()
myApplication = IdeaTestApplication.getInstance()
runWriteAction {
val jdkTableImpl = JavaAwareProjectJdkTableImpl.getInstanceEx()
val homePath = if (jdkTableImpl.internalJdk.homeDirectory!!.name == "jre") {
jdkTableImpl.internalJdk.homeDirectory!!.parent.path
} else {
jdkTableImpl.internalJdk.homePath!!
}
val javaSdk = JavaSdk.getInstance()
jdk18 = javaSdk.createJdk("1.8", homePath)
val internal = javaSdk.createJdk("IDEA jdk", homePath)
val jdkTable = ProjectJdkTable.getInstance()
jdkTable.addJdk(jdk18, testRootDisposable)
jdkTable.addJdk(internal, testRootDisposable)
KotlinSdkType.setUpIfNeeded()
}
}
protected fun warmUpProject(stats: Stats) {
val project = perfOpenHelloWorld(stats, WARM_UP)
try {
val perfHighlightFile = perfHighlightFile(project, "src/HelloMain.kt", stats, WARM_UP)
assertTrue("kotlin project has been not imported properly", perfHighlightFile.isNotEmpty())
} finally {
closeProject(project)
}
}
override fun tearDown() {
commitAllDocuments()
RunAll(
ThrowableRunnable { super.tearDown() },
ThrowableRunnable {
if (myProject != null) {
DaemonCodeAnalyzerSettings.getInstance().isImportHintEnabled = true // return default value to avoid unnecessary save
(StartupManager.getInstance(myProject!!) as StartupManagerImpl).checkCleared()
(DaemonCodeAnalyzer.getInstance(myProject!!) as DaemonCodeAnalyzerImpl).cleanupAfterTest()
closeProject(myProject!!)
myProject = null
}
}).run()
}
fun simpleFilename(fileName: String): String {
val lastIndexOf = fileName.lastIndexOf('/')
return if (lastIndexOf >= 0) fileName.substring(lastIndexOf + 1) else fileName
}
protected fun perfOpenKotlinProjectFast(stats: Stats) =
perfOpenKotlinProject(stats, fast = true)
protected fun perfOpenKotlinProject(stats: Stats, fast: Boolean = false) {
myProject = innerPerfOpenProject("kotlin", stats = stats, path = "../perfTestProject", note = "", fast = fast)
}
protected fun perfOpenHelloWorld(stats: Stats, note: String = ""): Project =
innerPerfOpenProject("helloKotlin", stats, note, path = "idea/testData/perfTest/helloKotlin", simpleModule = true)
protected fun innerPerfOpenProject(
name: String,
stats: Stats,
note: String,
path: String,
simpleModule: Boolean = false,
fast: Boolean = false
): Project {
val projectPath = File(path).canonicalPath
assertTrue("path $path does not exist, check README.md", File(projectPath).exists())
val warmUpIterations = if (fast) 0 else 1
val iterations = if (fast) 1 else 3
val projectManagerEx = ProjectManagerEx.getInstanceEx()
var lastProject: Project? = null
var counter = 0
stats.perfTest<Unit, Project>(
warmUpIterations = warmUpIterations,
iterations = iterations,
testName = "open project${if (note.isNotEmpty()) " $note" else ""}",
test = {
val project = if (!simpleModule) {
val project = projectManagerEx.loadProject(Paths.get(path), name)
assertNotNull("project $name at $path is not loaded", project)
val projectRootManager = ProjectRootManager.getInstance(project!!)
runWriteAction {
with(projectRootManager) {
projectSdk = jdk18
}
}
assertTrue("project $name at $path is not opened", projectManagerEx.openProject(project))
project
} else {
val project = projectManagerEx.loadAndOpenProject(projectPath)!!
initKotlinProject(project, projectPath, name)
project
}
(project as ProjectImpl).registerComponentImplementation(
FileEditorManager::class.java,
FileEditorManagerImpl::class.java
)
dispatchAllInvocationEvents()
with(StartupManager.getInstance(project) as StartupManagerImpl) {
ProjectRootManagerEx.getInstanceEx(project).markRootsForRefresh()
VirtualFileManager.getInstance().syncRefresh()
runStartupActivities()
runPostStartupActivities()
}
logMessage { "project $name is ${if (project.isInitialized) "initialized" else "not initialized"}" }
with(ChangeListManager.getInstance(project) as ChangeListManagerImpl) {
waitUntilRefreshed()
}
it.value = project
},
tearDown = {
it.value?.let { project ->
runAndMeasure("refresh gradle project $name") {
refreshGradleProjectIfNeeded(projectPath, project)
}
ApplicationManager.getApplication().executeOnPooledThread {
DumbService.getInstance(project).waitForSmartMode()
for (module in getModulesWithKotlinFiles(project)) {
module.getAndCacheLanguageLevelByDependencies()
}
}.get()
val modules = ModuleManager.getInstance(project).modules
assertTrue("project has to have at least one module", modules.isNotEmpty())
logMessage { "modules of $name: ${modules.map { m -> m.name }}" }
lastProject = project
VirtualFileManager.getInstance().syncRefresh()
runWriteAction {
project.save()
}
logMessage { "project '$name' successfully opened" }
// close all project but last - we're going to return and use it further
if (counter < warmUpIterations + iterations - 1) {
closeProject(project)
}
counter++
}
}
)
// indexing
lastProject?.let { project ->
invalidateLibraryCache(project)
CodeInsightTestFixtureImpl.ensureIndexesUpToDate(project)
dispatchAllInvocationEvents()
logMessage { "project $name is ${if (project.isInitialized) "initialized" else "not initialized"}" }
with(DumbService.getInstance(project)) {
queueTask(UnindexedFilesUpdater(project))
completeJustSubmittedTasks()
}
dispatchAllInvocationEvents()
Fixture.enableAnnotatorsAndLoadDefinitions(project)
}
return lastProject ?: error("unable to open project $name at $path")
}
private fun refreshGradleProjectIfNeeded(projectPath: String, project: Project) {
if (listOf("build.gradle.kts", "build.gradle").map { name -> Paths.get(projectPath, name).exists() }.find { e -> e } != true) return
ExternalProjectsManagerImpl.getInstance(project).setStoreExternally(true)
refreshGradleProject(projectPath, project)
//ExternalProjectsManagerImpl.getInstance(project).setStoreExternally(false)
dispatchAllInvocationEvents()
// WARNING: [VD] DO NOT SAVE PROJECT AS IT COULD PERSIST WRONG MODULES INFO
// runInEdtAndWait {
// PlatformTestUtil.saveProject(project)
// }
}
fun perfTypeAndAutocomplete(
stats: Stats,
fileName: String,
marker: String,
insertString: String,
surroundItems: String = "\n",
lookupElements: List<String>,
typeAfterMarker: Boolean = true,
revertChangesAtTheEnd: Boolean = true,
note: String = ""
) = perfTypeAndAutocomplete(
myProject!!, stats, fileName, marker, insertString, surroundItems,
lookupElements = lookupElements, typeAfterMarker = typeAfterMarker,
revertChangesAtTheEnd = revertChangesAtTheEnd, note = note
)
fun perfTypeAndAutocomplete(
project: Project,
stats: Stats,
fileName: String,
marker: String,
insertString: String,
surroundItems: String = "\n",
lookupElements: List<String>,
typeAfterMarker: Boolean = true,
revertChangesAtTheEnd: Boolean = true,
note: String = ""
) {
assertTrue("lookupElements has to be not empty", lookupElements.isNotEmpty())
stats.perfTest<Pair<String, Fixture>, Array<LookupElement>>(
warmUpIterations = 8,
iterations = 15,
testName = "typeAndAutocomplete ${notePrefix(note)}$fileName",
setUp = {
val fixture = openFixture(project, fileName)
val editor = fixture.editor
val initialText = editor.document.text
if (isAKotlinScriptFile(fileName)) {
runAndMeasure("update script dependencies for $fileName") {
ScriptConfigurationManager.updateScriptDependenciesSynchronously(fixture.psiFile, project)
}
}
val tasksIdx = editor.document.text.indexOf(marker)
assertTrue("marker '$marker' not found in $fileName", tasksIdx > 0)
if (typeAfterMarker) {
editor.caretModel.moveToOffset(tasksIdx + marker.length + 1)
} else {
editor.caretModel.moveToOffset(tasksIdx - 1)
}
for (surroundItem in surroundItems) {
EditorTestUtil.performTypingAction(editor, surroundItem)
}
editor.caretModel.moveToOffset(editor.caretModel.offset - if (typeAfterMarker) 1 else 2)
if (!typeAfterMarker) {
for (surroundItem in surroundItems) {
EditorTestUtil.performTypingAction(editor, surroundItem)
}
editor.caretModel.moveToOffset(editor.caretModel.offset - 2)
}
fixture.type(insertString)
it.setUpValue = Pair(initialText, fixture)
},
test = {
val fixture = it.setUpValue!!.second
it.value = fixture.complete()
},
tearDown = {
val items = it.value?.map { e -> e.lookupString }?.toList() ?: emptyList()
try {
for (lookupElement in lookupElements) {
assertTrue("'$lookupElement' has to be present in items", items.contains(lookupElement))
}
} finally {
it.setUpValue?.let { pair ->
pair.second.revertChanges(revertChangesAtTheEnd, pair.first)
}
commitAllDocuments()
}
}
)
}
fun perfTypeAndHighlight(
stats: Stats,
fileName: String,
marker: String,
insertString: String,
surroundItems: String = "\n",
typeAfterMarker: Boolean = true,
revertChangesAtTheEnd: Boolean = true,
note: String = ""
) = perfTypeAndHighlight(
myProject!!, stats, fileName, marker, insertString, surroundItems,
typeAfterMarker = typeAfterMarker,
revertChangesAtTheEnd = revertChangesAtTheEnd, note = note
)
fun perfTypeAndHighlight(
project: Project,
stats: Stats,
fileName: String,
marker: String,
insertString: String,
surroundItems: String = "\n",
typeAfterMarker: Boolean = true,
revertChangesAtTheEnd: Boolean = true,
note: String = ""
) {
stats.perfTest<Pair<String, Fixture>, List<HighlightInfo>>(
warmUpIterations = 8,
iterations = 15,
testName = "typeAndHighlight ${notePrefix(note)}$fileName",
setUp = {
val fixture = openFixture(project, fileName)
val editor = fixture.editor
val initialText = editor.document.text
if (isAKotlinScriptFile(fileName)) {
runAndMeasure("update script dependencies for $fileName") {
ScriptConfigurationManager.updateScriptDependenciesSynchronously(fixture.psiFile, project)
}
}
val tasksIdx = editor.document.text.indexOf(marker)
assertTrue("marker '$marker' not found in $fileName", tasksIdx > 0)
if (typeAfterMarker) {
editor.caretModel.moveToOffset(tasksIdx + marker.length + 1)
} else {
editor.caretModel.moveToOffset(tasksIdx - 1)
}
for (surroundItem in surroundItems) {
EditorTestUtil.performTypingAction(editor, surroundItem)
}
editor.caretModel.moveToOffset(editor.caretModel.offset - if (typeAfterMarker) 1 else 2)
if (!typeAfterMarker) {
for (surroundItem in surroundItems) {
EditorTestUtil.performTypingAction(editor, surroundItem)
}
editor.caretModel.moveToOffset(editor.caretModel.offset - 2)
}
fixture.type(insertString)
it.setUpValue = Pair(initialText, fixture)
},
test = {
val fixture = it.setUpValue!!.second
it.value = fixture.doHighlighting()
},
tearDown = {
it.value?.let { list ->
assertNotEmpty(list)
}
it.setUpValue?.let { pair ->
pair.second.revertChanges(revertChangesAtTheEnd, pair.first)
}
commitAllDocuments()
}
)
}
private fun initKotlinProject(
project: Project,
projectPath: String,
name: String
) {
val modulePath = "$projectPath/$name${ModuleFileType.DOT_DEFAULT_EXTENSION}"
val projectFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(File(projectPath))!!
val srcFile = projectFile.findChild("src")!!
val module = runWriteAction {
val projectRootManager = ProjectRootManager.getInstance(project)
with(projectRootManager) {
projectSdk = jdk18
}
val moduleManager = ModuleManager.getInstance(project)
val module = moduleManager.newModule(modulePath, ModuleTypeId.JAVA_MODULE)
PsiTestUtil.addSourceRoot(module, srcFile)
module
}
ConfigLibraryUtil.configureKotlinRuntimeAndSdk(module, jdk18)
}
protected fun perfHighlightFile(name: String, stats: Stats): List<HighlightInfo> =
perfHighlightFile(myProject!!, name, stats)
protected fun perfHighlightFile(
project: Project,
fileName: String,
stats: Stats,
note: String = ""
): List<HighlightInfo> {
return highlightFile {
val isWarmUp = note == WARM_UP
var highlightInfos: List<HighlightInfo> = emptyList()
stats.perfTest<EditorFile, List<HighlightInfo>>(
warmUpIterations = if (isWarmUp) 1 else 3,
iterations = if (isWarmUp) 2 else 10,
testName = "highlighting ${notePrefix(note)}${simpleFilename(fileName)}",
setUp = {
it.setUpValue = openFileInEditor(project, fileName)
},
test = {
val file = it.setUpValue
it.value = highlightFile(project, file!!.psiFile)
},
tearDown = {
highlightInfos = it.value ?: emptyList()
commitAllDocuments()
FileEditorManager.getInstance(project).closeFile(it.setUpValue!!.psiFile.virtualFile)
PsiManager.getInstance(project).dropPsiCaches()
}
)
highlightInfos
}
}
fun <T> highlightFile(block: () -> T): T {
var value: T? = null
IdentifierHighlighterPassFactory.doWithHighlightingEnabled {
value = block()
}
return value!!
}
private fun highlightFile(project: Project, psiFile: PsiFile): List<HighlightInfo> {
val document = FileDocumentManager.getInstance().getDocument(psiFile.virtualFile)!!
val editor = EditorFactory.getInstance().getEditors(document).first()
PsiDocumentManager.getInstance(project).commitAllDocuments()
return CodeInsightTestFixtureImpl.instantiateAndRun(psiFile, editor, ArrayUtilRt.EMPTY_INT_ARRAY, true)
}
protected fun perfScriptDependencies(name: String, stats: Stats, note: String = "") =
perfScriptDependencies(myProject!!, name, stats, note = note)
private fun perfScriptDependencies(
project: Project,
fileName: String,
stats: Stats,
note: String = ""
) {
if (!isAKotlinScriptFile(fileName)) return
stats.perfTest<EditorFile, EditorFile>(
testName = "updateScriptDependencies ${notePrefix(note)}${simpleFilename(fileName)}",
setUp = { it.setUpValue = openFileInEditor(project, fileName) },
test = {
ScriptConfigurationManager.updateScriptDependenciesSynchronously(it.setUpValue!!.psiFile, project)
it.value = it.setUpValue
},
tearDown = {
it.setUpValue?.let { ef -> cleanupCaches(project, ef.psiFile.virtualFile) }
it.value?.let { v -> assertNotNull(v) }
}
)
}
fun notePrefix(note: String) = if (note.isNotEmpty()) {
if (note.endsWith("/")) note else "$note "
} else ""
}
@@ -0,0 +1,94 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.testFramework
import com.intellij.lang.LanguageAnnotators
import com.intellij.lang.LanguageExtensionPoint
import com.intellij.lang.annotation.Annotator
import com.intellij.openapi.Disposable
import com.intellij.openapi.editor.Document
import com.intellij.openapi.extensions.ExtensionPointName
import com.intellij.openapi.fileEditor.FileDocumentManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.ex.ProjectManagerEx
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.impl.PsiDocumentManagerBase
import com.intellij.testFramework.ExtensionTestUtil
import com.intellij.testFramework.runInEdtAndWait
import com.intellij.util.ui.UIUtil
import org.jetbrains.kotlin.idea.parameterInfo.HintType
fun commitAllDocuments() {
val fileDocumentManager = FileDocumentManager.getInstance()
runInEdtAndWait {
fileDocumentManager.saveAllDocuments()
}
ProjectManagerEx.getInstanceEx().openProjects.forEach { project ->
val psiDocumentManagerBase = PsiDocumentManager.getInstance(project) as PsiDocumentManagerBase
runInEdtAndWait {
psiDocumentManagerBase.clearUncommittedDocuments()
psiDocumentManagerBase.commitAllDocuments()
}
}
}
fun commitDocument(project: Project, document: Document) {
val psiDocumentManagerBase = PsiDocumentManager.getInstance(project) as PsiDocumentManagerBase
runInEdtAndWait {
psiDocumentManagerBase.commitDocument(document)
}
}
fun saveDocument(document: Document) {
val fileDocumentManager = FileDocumentManager.getInstance()
runInEdtAndWait {
fileDocumentManager.saveDocument(document)
}
}
fun enableHints(enable: Boolean) =
HintType.values().forEach { it.option.set(enable) }
fun dispatchAllInvocationEvents() {
runInEdtAndWait {
UIUtil.dispatchAllInvocationEvents()
}
}
fun closeProject(project: Project) {
dispatchAllInvocationEvents()
val projectManagerEx = ProjectManagerEx.getInstanceEx()
projectManagerEx.forceCloseProject(project, true)
}
fun waitForAllEditorsFinallyLoaded(project: Project) {
// routing is obsolete in 192
}
fun replaceWithCustomHighlighter(parentDisposable: Disposable, fromImplementationClass: String, toImplementationClass: String) {
val pointName = ExtensionPointName.create<LanguageExtensionPoint<Annotator>>(LanguageAnnotators.EP_NAME)
val extensionPoint = pointName.getPoint(null)
val point = LanguageExtensionPoint<Annotator>()
point.language = "kotlin"
point.implementationClass = toImplementationClass
val extensions = extensionPoint.extensions
val filteredExtensions =
extensions.filter { it.language != "kotlin" || it.implementationClass != fromImplementationClass }
.toList()
// custom highlighter is already registered if filteredExtensions has the same size as extensions
if (filteredExtensions.size < extensions.size) {
ExtensionTestUtil.maskExtensions(pointName, filteredExtensions + listOf(point), parentDisposable)
}
}
fun logMessage(message: () -> String) {
println("-- ${message()}")
}
@@ -0,0 +1,146 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.formatter;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.impl.DocumentImpl;
import com.intellij.openapi.roots.LanguageLevelProjectExtension;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.testFramework.LightIdeaTestCase;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.idea.KotlinLanguage;
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase;
import org.jetbrains.kotlin.test.InTextDirectivesUtils;
import org.jetbrains.kotlin.test.KotlinTestUtils;
import org.jetbrains.kotlin.test.SettingsConfigurator;
import java.io.File;
import java.util.EnumMap;
import java.util.Map;
// Based on from com.intellij.psi.formatter.java.AbstractJavaFormatterTest
@SuppressWarnings("UnusedDeclaration")
public abstract class AbstractFormatterTest extends LightIdeaTestCase {
protected enum Action {REFORMAT, INDENT}
private interface TestFormatAction {
void run(PsiFile psiFile, int startOffset, int endOffset);
}
private static final Map<Action, TestFormatAction> ACTIONS = new EnumMap<>(Action.class);
static {
ACTIONS.put(Action.REFORMAT,
(psiFile, startOffset, endOffset) -> CodeStyleManager.getInstance(psiFile.getProject()).reformatText(psiFile, startOffset, endOffset));
ACTIONS.put(Action.INDENT,
(psiFile, startOffset, endOffset) -> CodeStyleManager.getInstance(psiFile.getProject()).adjustLineIndent(psiFile, startOffset));
}
private static final String BASE_PATH =
new File(PluginTestCaseBase.getTestDataPathBase(), "/formatter/").getAbsolutePath();
public TextRange myTextRange;
public TextRange myLineRange;
@Override
protected void setUp() throws Exception {
super.setUp();
LanguageLevelProjectExtension.getInstance(getProject()).setLanguageLevel(LanguageLevel.HIGHEST);
}
public void doTextTest(@NonNls String text, File fileAfter, String extension) throws IncorrectOperationException {
doTextTest(Action.REFORMAT, text, fileAfter, extension);
}
public void doTextTest(final Action action, final String text, File fileAfter, String extension) throws IncorrectOperationException {
final PsiFile file = createFile("A" + extension, text);
if (myLineRange != null) {
DocumentImpl document = new DocumentImpl(text);
myTextRange =
new TextRange(document.getLineStartOffset(myLineRange.getStartOffset()), document.getLineEndOffset(myLineRange.getEndOffset()));
}
final PsiDocumentManager manager = PsiDocumentManager.getInstance(getProject());
final Document document = manager.getDocument(file);
CommandProcessor.getInstance().executeCommand(getProject(), new Runnable() {
@Override
public void run() {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
document.replaceString(0, document.getTextLength(), text);
manager.commitDocument(document);
try {
TextRange rangeToUse = myTextRange;
if (rangeToUse == null) {
rangeToUse = file.getTextRange();
}
ACTIONS.get(action).run(file, rangeToUse.getStartOffset(), rangeToUse.getEndOffset());
}
catch (IncorrectOperationException e) {
assertTrue(e.getLocalizedMessage(), false);
}
}
});
}
}, "", "");
if (document == null) {
fail("Don't expect the document to be null");
return;
}
KotlinTestUtils.assertEqualsToFile(fileAfter, document.getText());
manager.commitDocument(document);
KotlinTestUtils.assertEqualsToFile(fileAfter, file.getText());
}
public void doTest(@NotNull String expectedFileNameWithExtension) throws Exception {
doTest(expectedFileNameWithExtension, false);
}
public void doTestInverted(@NotNull String expectedFileNameWithExtension) throws Exception {
doTest(expectedFileNameWithExtension, true);
}
public void doTest(@NotNull String expectedFileNameWithExtension, boolean inverted) throws Exception {
String testFileName = expectedFileNameWithExtension.substring(0, expectedFileNameWithExtension.indexOf("."));
String testFileExtension = expectedFileNameWithExtension.substring(expectedFileNameWithExtension.lastIndexOf("."));
String originalFileText = FileUtil.loadFile(new File(testFileName + testFileExtension), true);
CodeStyleSettings codeStyleSettings = FormatSettingsUtil.getSettings(getProject());
try {
Integer rightMargin = InTextDirectivesUtils.getPrefixedInt(originalFileText, "// RIGHT_MARGIN: ");
if (rightMargin != null) {
codeStyleSettings.setRightMargin(KotlinLanguage.INSTANCE, rightMargin);
}
SettingsConfigurator configurator = FormatSettingsUtil.createConfigurator(originalFileText, codeStyleSettings);
if (!inverted) {
configurator.configureSettings();
}
else {
configurator.configureInvertedSettings();
}
doTextTest(originalFileText, new File(expectedFileNameWithExtension), testFileExtension);
} finally {
codeStyleSettings.clearCodeStyleSettings();
}
}
}
@@ -0,0 +1,68 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.formatter;
import com.intellij.application.options.CodeStyle;
import com.intellij.openapi.editor.CaretModel;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.testFramework.EditorTestUtil;
import com.intellij.testFramework.LightCodeInsightTestCase;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.test.KotlinTestUtils;
import org.jetbrains.kotlin.test.SettingsConfigurator;
import java.io.File;
public abstract class AbstractTypingIndentationTestBase extends LightCodeInsightTestCase {
public void doNewlineTest(String afterFilePath) throws Exception {
doNewlineTest(afterFilePath, false);
}
public void doNewlineTestWithInvert(String afterInvFilePath) throws Exception {
doNewlineTest(afterInvFilePath, true);
}
public void doNewlineTest(String afterFilePath, boolean inverted) throws Exception {
String testFileName = afterFilePath.substring(0, afterFilePath.indexOf("."));
String testFileExtension = afterFilePath.substring(afterFilePath.lastIndexOf("."));
String originFilePath = testFileName + testFileExtension;
String originalFileText = FileUtil.loadFile(new File(originFilePath), true);
try {
SettingsConfigurator configurator = FormatSettingsUtil.createConfigurator(originalFileText, getProject());
if (!inverted) {
configurator.configureSettings();
}
else {
configurator.configureInvertedSettings();
}
doNewlineTest(originFilePath, afterFilePath);
}
finally {
CodeStyle.getSettings(getProject()).clearCodeStyleSettings();
}
}
private void doNewlineTest(String beforeFilePath, String afterFilePath) {
configureByFile(beforeFilePath);
type('\n');
CaretModel caretModel = getEditor().getCaretModel();
int offset = caretModel.getOffset();
String actualTextWithCaret = new StringBuilder(getEditor().getDocument().getText()).insert(offset, EditorTestUtil.CARET_TAG).toString();
KotlinTestUtils.assertEqualsToFile(new File(afterFilePath), actualTextWithCaret);
}
@NotNull
@Override
protected String getTestDataPath() {
return "";
}
}
@@ -0,0 +1,79 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.actions
import com.intellij.ide.actions.CopyReferenceAction
import com.intellij.psi.PsiElement
import com.intellij.testFramework.LightCodeInsightTestCase
import org.jetbrains.kotlin.psi.KtNamedDeclaration
import org.jetbrains.kotlin.psi.KtVisitorVoid
import org.jetbrains.kotlin.test.JUnit3WithIdeaConfigurationRunner
import org.junit.runner.RunWith
import java.util.*
@RunWith(JUnit3WithIdeaConfigurationRunner::class)
class QualifiedNamesTest : LightCodeInsightTestCase() {
fun testClassRef() {
configureFromFileText(
"class.kt",
"""
package foo.bar
class Klass {
class Nested
companion object {
}
}
object Object {
}
val anonymous = object {
}
"""
)
assertEquals(listOf("foo.bar.Klass", "foo.bar.Klass.Nested", "foo.bar.Klass.Companion", "foo.bar.Object", "foo.bar.ClassKt#getAnonymous", null),
getQualifiedNamesForDeclarations())
}
fun testFunRef() {
configureFromFileText(
"fun.kt",
"""
package foo.bar
class Klass {
fun memberFun() {
}
val memberVal = ":)"
}
fun topLevelFun()
val topLevelVal = ":)"
"""
)
assertEquals(listOf("foo.bar.Klass", "foo.bar.Klass#memberFun", "foo.bar.Klass#getMemberVal", "foo.bar.FunKt#topLevelFun", "foo.bar.FunKt#getTopLevelVal"),
getQualifiedNamesForDeclarations())
}
private fun getQualifiedNamesForDeclarations(): List<String?> {
val result = ArrayList<String?>()
file.accept(object : KtVisitorVoid() {
override fun visitElement(element: PsiElement) {
element.acceptChildren(this)
}
override fun visitNamedDeclaration(declaration: KtNamedDeclaration) {
result.add(CopyReferenceAction.elementToFqn(declaration))
super.visitNamedDeclaration(declaration)
}
})
return result
}
}
@@ -0,0 +1,127 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.codeInsight.moveUpDown
import com.intellij.codeInsight.editorActions.moveLeftRight.MoveElementLeftAction
import com.intellij.codeInsight.editorActions.moveLeftRight.MoveElementRightAction
import com.intellij.codeInsight.editorActions.moveUpDown.MoveStatementDownAction
import com.intellij.codeInsight.editorActions.moveUpDown.MoveStatementUpAction
import com.intellij.codeInsight.editorActions.moveUpDown.StatementUpDownMover
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.runWriteAction
import com.intellij.openapi.editor.actionSystem.EditorAction
import com.intellij.openapi.extensions.Extensions
import com.intellij.openapi.util.io.FileUtil
import com.intellij.testFramework.LightCodeInsightTestCase
import com.intellij.testFramework.LightPlatformCodeInsightTestCase
import junit.framework.ComparisonFailure
import junit.framework.TestCase
import org.jetbrains.kotlin.formatter.FormatSettingsUtil
import org.jetbrains.kotlin.idea.codeInsight.upDownMover.KotlinDeclarationMover
import org.jetbrains.kotlin.idea.codeInsight.upDownMover.KotlinExpressionMover
import org.jetbrains.kotlin.idea.core.script.isScriptDependenciesUpdaterDisabled
import org.jetbrains.kotlin.test.InTextDirectivesUtils
import org.jetbrains.kotlin.test.KotlinTestUtils
import java.io.File
abstract class AbstractMoveStatementTest : AbstractCodeMoverTest() {
protected fun doTestClassBodyDeclaration(path: String) {
doTest(path, KotlinDeclarationMover::class.java)
}
protected fun doTestExpression(path: String) {
doTest(path, KotlinExpressionMover::class.java)
}
private fun doTest(path: String, defaultMoverClass: Class<out StatementUpDownMover>) {
doTest(path) { isApplicableExpected, direction ->
val movers = Extensions.getExtensions(StatementUpDownMover.STATEMENT_UP_DOWN_MOVER_EP)
val info = StatementUpDownMover.MoveInfo()
val actualMover = movers.firstOrNull {
it.checkAvailable(editor, file, info, direction == "down")
} ?: error("No mover found")
assertEquals("Unmatched movers", defaultMoverClass.name, actualMover::class.java.name)
assertEquals("Invalid applicability", isApplicableExpected, info.toMove2 != null)
}
}
}
abstract class AbstractMoveLeftRightTest : AbstractCodeMoverTest() {
protected fun doTest(path: String) {
doTest(path) { _, _ -> }
}
}
abstract class AbstractCodeMoverTest : LightCodeInsightTestCase() {
override fun setUp() {
super.setUp()
ApplicationManager.getApplication().isScriptDependenciesUpdaterDisabled = true
}
override fun tearDown() {
ApplicationManager.getApplication().isScriptDependenciesUpdaterDisabled = false
super.tearDown()
}
protected fun doTest(path: String, isApplicableChecker: (isApplicableExpected: Boolean, direction: String) -> Unit) {
configureByFile(path)
val fileText = FileUtil.loadFile(File(path), true)
val direction = InTextDirectivesUtils.findStringWithPrefixes(fileText, "// MOVE: ")
?: error("No MOVE directive found")
val action = when (direction) {
"up" -> MoveStatementUpAction()
"down" -> MoveStatementDownAction()
"left" -> MoveElementLeftAction()
"right" -> MoveElementRightAction()
else -> error("Unknown direction: $direction")
}
val isApplicableString = InTextDirectivesUtils.findStringWithPrefixes(fileText, "// IS_APPLICABLE: ")
val isApplicableExpected = isApplicableString == null || isApplicableString == "true"
isApplicableChecker(isApplicableExpected, direction)
invokeAndCheck(fileText, path, action, isApplicableExpected)
}
private fun invokeAndCheck(fileText: String, path: String, action: EditorAction, isApplicableExpected: Boolean) {
val project = editor.project!!
val codeStyleSettings = FormatSettingsUtil.getSettings(project)
val configurator = FormatSettingsUtil.createConfigurator(fileText, codeStyleSettings)
configurator.configureSettings()
try {
val dataContext = currentEditorDataContext
val before = editor.document.text
runWriteAction { action.actionPerformed(editor, dataContext) }
val after = editor.document.text
val actionDoesNothing = after == before
TestCase.assertEquals(isApplicableExpected, !actionDoesNothing)
if (isApplicableExpected) {
val afterFilePath = path + ".after"
try {
checkResultByFile(afterFilePath)
}
catch (e: ComparisonFailure) {
KotlinTestUtils.assertEqualsToFile(File(afterFilePath), editor)
}
}
}
finally {
codeStyleSettings.clearCodeStyleSettings()
}
}
override fun getTestDataPath() = ""
}
@@ -0,0 +1,75 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence
import com.intellij.debugger.streams.test.StreamChainBuilderTestCase
import com.intellij.debugger.streams.wrapper.StreamChain
import com.intellij.debugger.streams.wrapper.StreamChainBuilder
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.roots.impl.libraries.ProjectLibraryTable
import com.intellij.testFramework.LightPlatformTestCase
import com.intellij.testFramework.PsiTestUtil
import junit.framework.TestCase
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime
import org.jetbrains.kotlin.idea.caches.project.LibraryModificationTracker
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
import java.io.File
import java.nio.file.Paths
abstract class KotlinPsiChainBuilderTestCase(private val relativePath: String) : StreamChainBuilderTestCase() {
override fun getTestDataPath(): String =
Paths.get(File("").absolutePath, "idea/testData/debugger/sequence/psi/$relativeTestPath/").toString()
override fun getFileExtension(): String = ".kt"
abstract val kotlinChainBuilder: StreamChainBuilder
override fun getChainBuilder(): StreamChainBuilder = kotlinChainBuilder
private val stdLibName = "kotlin-stdlib"
protected abstract fun doTest()
final override fun getRelativeTestPath(): String = relativePath
override fun setUp() {
super.setUp()
ApplicationManager.getApplication().runWriteAction {
if (ProjectLibraryTable.getInstance(project).getLibraryByName(stdLibName) == null) {
val stdLibPath = ForTestCompileRuntime.runtimeJarForTests()
PsiTestUtil.addLibrary(
testRootDisposable,
module,
stdLibName,
stdLibPath.parent,
stdLibPath.name
)
}
}
LibraryModificationTracker.getInstance(project).incModificationCount()
}
override fun getProjectJDK(): Sdk {
return PluginTestCaseBase.mockJdk9()
}
abstract class Positive(relativePath: String) : KotlinPsiChainBuilderTestCase(relativePath) {
override fun doTest() {
val chains = buildChains()
checkChains(chains)
}
private fun checkChains(chains: MutableList<StreamChain>) {
TestCase.assertFalse(chains.isEmpty())
}
}
abstract class Negative(relativePath: String) : KotlinPsiChainBuilderTestCase(relativePath) {
override fun doTest() {
val elementAtCaret = configureAndGetElementAtCaret()
TestCase.assertFalse(chainBuilder.isChainExists(elementAtCaret))
TestCase.assertTrue(chainBuilder.build(elementAtCaret).isEmpty())
}
}
}
@@ -0,0 +1,86 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.editor;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.testFramework.EditorTestUtil;
import com.intellij.testFramework.LightCodeInsightTestCase;
import org.jetbrains.kotlin.formatter.FormatSettingsUtil;
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase;
import org.jetbrains.kotlin.test.JUnit3WithIdeaConfigurationRunner;
import org.jetbrains.kotlin.test.KotlinTestUtils;
import org.jetbrains.kotlin.test.SettingsConfigurator;
import org.junit.runner.RunWith;
import java.io.File;
@RunWith(JUnit3WithIdeaConfigurationRunner.class)
public class KotlinCommenterTest extends LightCodeInsightTestCase {
private static final String BASE_PATH =
new File(PluginTestCaseBase.getTestDataPathBase(), "/editor/commenter/").getAbsolutePath();
public void testGenerateDocComment() throws Exception {
doNewLineTypingTest();
}
public void testNewLineInComment() throws Exception {
doNewLineTypingTest();
}
public void testNewLineInTag() throws Exception {
doNewLineTypingTest();
}
public void testNotFirstColumnWithSpace() throws Exception {
doLineCommentTest();
}
public void testNotFirstColumnWithoutSpace() throws Exception {
doLineCommentTest();
}
private void doNewLineTypingTest() throws Exception {
configure();
EditorTestUtil.performTypingAction(getEditor(), '\n');
check();
}
private void doLineCommentTest() throws Exception {
configure();
CodeStyleSettings codeStyleSettings = FormatSettingsUtil.getSettings(getProject());
try {
String text = getFile().getText();
SettingsConfigurator configurator = FormatSettingsUtil.createConfigurator(text, codeStyleSettings);
configurator.configureSettings();
executeAction("CommentByLineComment");
} finally {
codeStyleSettings.clearCodeStyleSettings();
}
check();
}
private void configure() throws Exception {
configureFromFileText("a.kt", loadFile(getTestName(true) + ".kt"));
}
private void check() {
File afterFile = getTestFile(getTestName(true) + "_after.kt");
KotlinTestUtils.assertEqualsToFile(afterFile, getEditor(), false);
}
private static File getTestFile(String name) {
return new File(BASE_PATH, name);
}
private static String loadFile(String name) throws Exception {
File file = getTestFile(name);
return FileUtil.loadFile(file, true);
}
}
@@ -0,0 +1,935 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.editor
import com.intellij.application.options.CodeStyle
import com.intellij.openapi.project.Project
import com.intellij.testFramework.EditorTestUtil
import com.intellij.testFramework.LightCodeInsightTestCase
import com.intellij.testFramework.LightPlatformCodeInsightTestCase
import com.intellij.testFramework.LightPlatformTestCase
import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.idea.formatter.KotlinStyleGuideCodeStyle
import org.jetbrains.kotlin.idea.formatter.ktCodeStyleSettings
import org.jetbrains.kotlin.test.JUnit3WithIdeaConfigurationRunner
import org.junit.runner.RunWith
@RunWith(JUnit3WithIdeaConfigurationRunner::class)
class TypedHandlerTest : LightCodeInsightTestCase() {
private val dollar = '$'
fun testTypeStringTemplateStart() = doTypeTest(
'{',
"""val x = "$<caret>" """,
"""val x = "$dollar{}" """
)
fun testAutoIndentRightOpenBrace() = doTypeTest(
'{',
"fun test() {\n" +
"<caret>\n" +
"}",
"fun test() {\n" +
" {<caret>}\n" +
"}"
)
fun testAutoIndentLeftOpenBrace() = doTypeTest(
'{',
"fun test() {\n" +
" <caret>\n" +
"}",
"fun test() {\n" +
" {<caret>}\n" +
"}"
)
fun testTypeStringTemplateStartWithCloseBraceAfter() = doTypeTest(
'{',
"""fun foo() { "$<caret>" }""",
"""fun foo() { "$dollar{}" }"""
)
fun testTypeStringTemplateStartBeforeStringWithExistingDollar() = doTypeTest(
'{',
"""fun foo() { "$<caret>something" }""",
"""fun foo() { "$dollar{something" }"""
)
fun testTypeStringTemplateStartBeforeStringWithNoDollar() = doTypeTest(
"$dollar{",
"""fun foo() { "<caret>something" }""",
"""fun foo() { "$dollar{<caret>}something" }"""
)
fun testTypeStringTemplateWithUnmatchedBrace() = doTypeTest(
"$dollar{",
"""val a = "<caret>bar}foo"""",
"""val a = "$dollar{<caret>bar}foo""""
)
fun testTypeStringTemplateWithUnmatchedBraceComplex() = doTypeTest(
"$dollar{",
"""val a = "<caret>bar + more}foo"""",
"""val a = "$dollar{<caret>}bar + more}foo""""
)
fun testTypeStringTemplateStartInStringWithBraceLiterals() = doTypeTest(
"$dollar{",
"""val test = "{ code <caret>other }"""",
"""val test = "{ code $dollar{<caret>}other }""""
)
fun testTypeStringTemplateStartInEmptyString() = doTypeTest(
'{',
"""fun foo() { "$<caret>" }""",
"""fun foo() { "$dollar{<caret>}" }"""
)
fun testKT3575() = doTypeTest(
'{',
"""val x = "$<caret>]" """,
"""val x = "$dollar{}]" """
)
fun testAutoCloseRawStringInEnd() = doTypeTest(
'"',
"""val x = ""<caret>""",
"""val x = ""${'"'}<caret>""${'"'}"""
)
fun testNoAutoCloseRawStringInEnd() = doTypeTest(
'"',
"""val x = ""${'"'}<caret>""",
"""val x = ""${'"'}""""
)
fun testAutoCloseRawStringInMiddle() = doTypeTest(
'"',
"""
val x = ""<caret>
val y = 12
""".trimIndent(),
"""
val x = ""${'"'}<caret>""${'"'}
val y = 12
""".trimIndent()
)
fun testNoAutoCloseBetweenMultiQuotes() = doTypeTest(
'"',
"""val x = ""${'"'}<caret>${'"'}""/**/""",
"""val x = ""${'"'}${'"'}<caret>""/**/"""
)
fun testNoAutoCloseBetweenMultiQuotes1() = doTypeTest(
'"',
"""val x = ""${'"'}"<caret>"${'"'}/**/""",
"""val x = ""${'"'}""<caret>${'"'}/**/"""
)
fun testNoAutoCloseAfterEscape() = doTypeTest(
'"',
"""val x = "\""<caret>""",
"""val x = "\""${'"'}<caret>""""
)
fun testAutoCloseBraceInFunctionDeclaration() = doTypeTest(
'{',
"fun foo() <caret>",
"fun foo() {<caret>}"
)
fun testAutoCloseBraceInLocalFunctionDeclaration() = doTypeTest(
'{',
"fun foo() {\n" +
" fun bar() <caret>\n" +
"}",
"fun foo() {\n" +
" fun bar() {<caret>}\n" +
"}"
)
fun testAutoCloseBraceInAssignment() = doTypeTest(
'{',
"fun foo() {\n" +
" val a = <caret>\n" +
"}",
"fun foo() {\n" +
" val a = {<caret>}\n" +
"}"
)
fun testDoNotAutoCloseBraceInUnfinishedIfSurroundOnSameLine() = doTypeTest(
'{',
"fun foo() {\n" +
" if() <caret>foo()\n" +
"}",
"fun foo() {\n" +
" if() {foo()\n" +
"}"
)
fun testDoNotAutoCloseBraceInUnfinishedElseSurroundOnSameLine() = doTypeTest(
'{',
"fun foo() {\n" +
" if(true) {} else <caret>foo()\n" +
"}",
"fun foo() {\n" +
" if(true) {} else {foo()\n" +
"}"
)
fun testDoNotAutoCloseBraceInUnfinishedTryOnSameLine() = doTypeTest(
'{',
"fun foo() {\n" +
" try <caret>foo()\n" +
"}",
"fun foo() {\n" +
" try {foo()\n" +
"}"
)
fun testDoNotAutoCloseBraceInUnfinishedCatchOnSameLine() = doTypeTest(
'{',
"fun foo() {\n" +
" try {} catch (e: Exception) <caret>foo()\n" +
"}",
"fun foo() {\n" +
" try {} catch (e: Exception) {foo()\n" +
"}"
)
fun testDoNotAutoCloseBraceInUnfinishedFinallyOnSameLine() = doTypeTest(
'{',
"fun foo() {\n" +
" try {} catch (e: Exception) finally <caret>foo()\n" +
"}",
"fun foo() {\n" +
" try {} catch (e: Exception) finally {foo()\n" +
"}"
)
fun testDoNotAutoCloseBraceInUnfinishedWhileSurroundOnSameLine() = doTypeTest(
'{',
"fun foo() {\n" +
" while() <caret>foo()\n" +
"}",
"fun foo() {\n" +
" while() {foo()\n" +
"}"
)
fun testDoNotAutoCloseBraceInUnfinishedWhileSurroundOnNewLine() = doTypeTest(
'{',
"fun foo() {\n" +
" while()\n" +
"<caret>\n" +
" foo()\n" +
"}",
"fun foo() {\n" +
" while()\n" +
" {\n" +
" foo()\n" +
"}"
)
fun testDoNotAutoCloseBraceInUnfinishedIfSurroundOnOtherLine() = doTypeTest(
'{',
"fun foo() {\n" +
" if(true) <caret>\n" +
" foo()\n" +
"}",
"fun foo() {\n" +
" if(true) {<caret>\n" +
" foo()\n" +
"}"
)
fun testDoNotAutoCloseBraceInUnfinishedElseSurroundOnOtherLine() = doTypeTest(
'{',
"fun foo() {\n" +
" if(true) {} else <caret>\n" +
" foo()\n" +
"}",
"fun foo() {\n" +
" if(true) {} else {<caret>\n" +
" foo()\n" +
"}"
)
fun testDoNotAutoCloseBraceInUnfinishedTryOnOtherLine() = doTypeTest(
'{',
"fun foo() {\n" +
" try <caret>\n" +
" foo()\n" +
"}",
"fun foo() {\n" +
" try {<caret>\n" +
" foo()\n" +
"}"
)
fun testDoNotAutoCloseBraceInUnfinishedIfSurroundOnNewLine() = doTypeTest(
'{',
"fun foo() {\n" +
" if(true)\n" +
" <caret>\n" +
" foo()\n" +
"}",
"fun foo() {\n" +
" if(true)\n" +
" {<caret>\n" +
" foo()\n" +
"}"
)
fun testDoNotAutoCloseBraceInUnfinishedElseSurroundOnNewLine() = doTypeTest(
'{',
"fun foo() {\n" +
" if(true) {} else\n" +
" <caret>\n" +
" foo()\n" +
"}",
"fun foo() {\n" +
" if(true) {} else\n" +
" {<caret>\n" +
" foo()\n" +
"}"
)
fun testDoNotAutoCloseBraceInUnfinishedTryOnNewLine() = doTypeTest(
'{',
"fun foo() {\n" +
" try\n" +
" <caret>\n" +
" foo()\n" +
"}",
"fun foo() {\n" +
" try\n" +
" {<caret>\n" +
" foo()\n" +
"}"
)
fun testAutoCloseBraceInsideFor() = doTypeTest(
'{',
"fun foo() {\n" +
" for (elem in some.filter <caret>) {\n" +
" }\n" +
"}",
"fun foo() {\n" +
" for (elem in some.filter {<caret>}) {\n" +
" }\n" +
"}"
)
fun testAutoCloseBraceInsideForAfterCloseParen() = doTypeTest(
'{',
"fun foo() {\n" +
" for (elem in some.foo(true) <caret>) {\n" +
" }\n" +
"}",
"fun foo() {\n" +
" for (elem in some.foo(true) {<caret>}) {\n" +
" }\n" +
"}"
)
fun testAutoCloseBraceBeforeIf() = doTypeTest(
'{',
"fun foo() {\n" +
" <caret>if (true) {}\n" +
"}",
"fun foo() {\n" +
" {<caret>if (true) {}\n" +
"}"
)
fun testAutoCloseBraceInIfCondition() = doTypeTest(
'{',
"fun foo() {\n" +
" if (some.hello (12) <caret>)\n" +
"}",
"fun foo() {\n" +
" if (some.hello (12) {<caret>})\n" +
"}"
)
fun testInsertSpaceAfterRightBraceOfNestedLambda() = doTypeTest(
'{',
"val t = Array(100) { Array(200) <caret>}",
"val t = Array(100) { Array(200) {<caret>} }"
)
fun testAutoInsertParenInStringLiteral() = doTypeTest(
'(',
"""fun f() { println("$dollar{f<caret>}") }""",
"""fun f() { println("$dollar{f(<caret>)}") }"""
)
fun testAutoInsertParenInCode() = doTypeTest(
'(',
"""fun f() { val a = f<caret> }""",
"""fun f() { val a = f(<caret>) }"""
)
fun testSplitStringByEnter() = doTypeTest(
'\n',
"""val s = "foo<caret>bar"""",
"val s = \"foo\" +\n" +
" \"bar\""
)
fun testSplitStringByEnterEmpty() = doTypeTest(
'\n',
"""val s = "<caret>"""",
"val s = \"\" +\n" +
" \"\""
)
fun testSplitStringByEnterBeforeEscapeSequence() = doTypeTest(
'\n',
"""val s = "foo<caret>\nbar"""",
"val s = \"foo\" +\n" +
" \"\\nbar\""
)
fun testSplitStringByEnterBeforeSubstitution() = doTypeTest(
'\n',
"""val s = "foo<caret>${dollar}bar"""",
"val s = \"foo\" +\n" +
" \"${dollar}bar\""
)
fun testSplitStringByEnterAddParentheses() = doTypeTest(
'\n',
"""val l = "foo<caret>bar".length()""",
"val l = (\"foo\" +\n" +
" \"bar\").length()"
)
fun testSplitStringByEnterExistingParentheses() = doTypeTest(
'\n',
"""val l = ("asdf" + "foo<caret>bar").length()""",
"val l = (\"asdf\" + \"foo\" +\n" +
" \"bar\").length()"
)
fun testTypeLtInFunDeclaration() {
doLtGtTest("fun <caret>")
}
fun testTypeLtInOngoingConstructorCall() {
doLtGtTest("fun test() { Collection<caret> }")
}
fun testTypeLtInClassDeclaration() {
doLtGtTest("class Some<caret> {}")
}
fun testTypeLtInPropertyType() {
doLtGtTest("val a: List<caret> ")
}
fun testTypeLtInExtensionFunctionReceiver() {
doLtGtTest("fun <T> Collection<caret> ")
}
fun testTypeLtInFunParam() {
doLtGtTest("fun some(a : HashSet<caret>)")
}
fun testTypeLtInFun() {
doLtGtTestNoAutoClose("fun some() { <<caret> }")
}
fun testTypeLtInLess() {
doLtGtTestNoAutoClose("fun some() { val a = 12; a <<caret> }")
}
fun testColonOfSuperTypeList() {
doTypeTest(
':',
"""
|open class A
|class B
|<caret>
""",
"""
|open class A
|class B
| :<caret>
""")
}
fun testColonOfSuperTypeListInObject() {
doTypeTest(
':',
"""
|interface A
|object B
|<caret>
""",
"""
|interface A
|object B
| :<caret>
""")
}
fun testColonOfSuperTypeListInCompanionObject() {
doTypeTest(
':',
"""
|interface A
|class B {
| companion object
| <caret>
|}
""",
"""
|interface A
|class B {
| companion object
| :<caret>
|}
""")
}
fun testColonOfSuperTypeListBeforeBody() {
doTypeTest(
':',
"""
|open class A
|class B
|<caret> {
|}
""",
"""
|open class A
|class B
| :<caret> {
|}
""")
}
fun testColonOfSuperTypeListNotNullIndent() {
doTypeTest(
':',
"""
|fun test() {
| open class A
| class B
| <caret>
|}
""",
"""
|fun test() {
| open class A
| class B
| :<caret>
|}
""")
}
fun testChainCallContinueWithDot() {
doTypeTest(
'.',
"""
|class Test{ fun test() = this }
|fun some() {
| Test()
| <caret>
|}
""",
"""
|class Test{ fun test() = this }
|fun some() {
| Test()
| .<caret>
|}
""")
}
fun testChainCallContinueWithSafeCall() {
doTypeTest(
'.',
"""
|class Test{ fun test() = this }
|fun some() {
| Test()
| ?<caret>
|}
""",
"""
|class Test{ fun test() = this }
|fun some() {
| Test()
| ?.<caret>
|}
""")
}
fun testContinueWithElvis() {
doTypeTest(
':',
"""
|fun test(): Any? = null
|fun some() {
| test()
| ?<caret>
|}
""",
"""
|fun test(): Any? = null
|fun some() {
| test()
| ?:<caret>
|}
"""
)
}
fun testContinueWithOr() {
doTypeTest(
'|',
"""
|fun some() {
| if (true
| |<caret>)
|}
""",
"""
|fun some() {
| if (true
| ||<caret>)
|}
"""
)
}
fun testContinueWithAnd() {
doTypeTest(
'&',
"""
|fun some() {
| val test = true
| &<caret>
|}
""",
"""
|fun some() {
| val test = true
| &&<caret>
|}
"""
)
}
fun testSpaceAroundRange() {
doTypeTest(
'.',
"""
| val test = 1 <caret>
""",
"""
| val test = 1 .<caret>
"""
)
}
fun testIndentBeforeElseWithBlock() {
doTypeTest(
'\n',
"""
|fun test(b: Boolean) {
| if (b) {
| }<caret>
| else if (!b) {
| }
|}
""",
"""
|fun test(b: Boolean) {
| if (b) {
| }
| <caret>
| else if (!b) {
| }
|}
"""
)
}
fun testIndentBeforeElseWithoutBlock() {
doTypeTest(
'\n',
"""
|fun test(b: Boolean) {
| if (b)
| foo()<caret>
| else {
| }
|}
""",
"""
|fun test(b: Boolean) {
| if (b)
| foo()
| <caret>
| else {
| }
|}
"""
)
}
fun testIndentOnFinishedVariableEndAfterEquals() {
doTypeTest(
'\n',
"""
|fun test() {
| val a =<caret>
| foo()
|}
""",
"""
|fun test() {
| val a =
| <caret>
| foo()
|}
"""
)
}
fun testIndentNotFinishedVariableEndAfterEquals() {
doTypeTest(
'\n',
"""
|fun test() {
| val a =<caret>
|}
""",
"""
|fun test() {
| val a =
| <caret>
|}
"""
)
}
fun testSmartEnterWithTabsOnConstructorParameters() {
doTypeTest(
'\n',
"""
|class A(
| a: Int,<caret>
|)
""",
"""
|class A(
| a: Int,
| <caret>
|)
""",
enableSmartEnterWithTabs()
)
}
fun testSmartEnterWithTabsInMethodParameters() {
doTypeTest(
'\n',
"""
|fun method(
| arg1: String,<caret>
|) {}
""",
"""
|fun method(
| arg1: String,
| <caret>
|) {}
""",
enableSmartEnterWithTabs()
)
}
fun testAutoIndentInWhenClause() {
doTypeTest(
'\n',
"""
|fun test() {
| when (2) {
| is Int -><caret>
| }
|}
""",
"""
|fun test() {
| when (2) {
| is Int ->
| <caret>
| }
|}
"""
)
}
fun testEnterInFunctionWithExpressionBody() {
doTypeTest(
'\n',
"""
|fun test() =<caret>
""",
"""
|fun test() =
| <caret>
""",
ENABLE_KOTLIN_OFFICIAL_CODE_STYLE
)
}
fun testEnterInMultiDeclaration() {
doTypeTest(
'\n',
"""
|fun test() {
| val (a, b) =<caret>
|}
""",
"""
|fun test() {
| val (a, b) =
| <caret>
|}
""",
ENABLE_KOTLIN_OFFICIAL_CODE_STYLE
)
}
fun testEnterInVariableDeclaration() {
doTypeTest(
'\n',
"""
|val test =<caret>
""",
"""
|val test =
| <caret>
""",
ENABLE_KOTLIN_OFFICIAL_CODE_STYLE
)
}
fun testMoveThroughGT() {
configureFromFileText("a.kt", "val a: List<Set<Int<caret>>>")
EditorTestUtil.performTypingAction(editor, '>')
EditorTestUtil.performTypingAction(editor, '>')
checkResultByText("val a: List<Set<Int>><caret>")
}
fun testCharClosingQuote() {
doTypeTest('\'', "val c = <caret>", "val c = ''")
}
private fun enableSmartEnterWithTabs(): (Project) -> Unit = {
val indentOptions = CodeStyle.getSettings(it).getIndentOptions(KotlinFileType.INSTANCE)
indentOptions.USE_TAB_CHARACTER = true
indentOptions.SMART_TABS = true
}
private fun doTypeTest(ch: Char, beforeText: String, afterText: String, settingsModifier: ((Project) -> Unit)? = null) {
doTypeTest(ch.toString(), beforeText, afterText, settingsModifier)
}
private fun doTypeTest(text: String, beforeText: String, afterText: String, settingsModifier: ((Project) -> Unit)? = null) {
try {
if (settingsModifier != null) {
settingsModifier(project)
}
configureFromFileText("a.kt", beforeText.trimMargin())
for (ch in text) {
EditorTestUtil.performTypingAction(editor, ch)
}
checkResultByText(afterText.trimMargin())
} finally {
if (settingsModifier != null) {
CodeStyle.getSettings(project).clearCodeStyleSettings()
}
}
}
private fun doLtGtTestNoAutoClose(initText: String) {
doLtGtTest(initText, false)
}
private fun doLtGtTest(initText: String, shouldCloseBeInsert: Boolean) {
configureFromFileText("a.kt", initText)
EditorTestUtil.performTypingAction(editor, '<')
checkResultByText(if (shouldCloseBeInsert) initText.replace("<caret>", "<<caret>>") else initText.replace("<caret>", "<<caret>"))
EditorTestUtil.performTypingAction(editor, EditorTestUtil.BACKSPACE_FAKE_CHAR)
checkResultByText(initText)
}
private fun doLtGtTest(initText: String) {
doLtGtTest(initText, true)
}
companion object {
private val ENABLE_KOTLIN_OFFICIAL_CODE_STYLE: (Project) -> Unit = {
val settings = ktCodeStyleSettings(it)?.all ?: error("No Settings")
KotlinStyleGuideCodeStyle.apply(settings)
}
}
}
@@ -0,0 +1,54 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.kdoc;
import com.intellij.codeInsight.TargetElementUtil;
import com.intellij.ide.startup.impl.StartupManagerImpl;
import com.intellij.openapi.startup.StartupManager;
import com.intellij.psi.PsiElement;
import com.intellij.refactoring.rename.RenameProcessor;
import com.intellij.testFramework.LightCodeInsightTestCase;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase;
import org.jetbrains.kotlin.test.JUnit3WithIdeaConfigurationRunner;
import org.junit.runner.RunWith;
@RunWith(JUnit3WithIdeaConfigurationRunner.class)
public class KdocRenameTest extends LightCodeInsightTestCase {
@NotNull
@Override
protected String getTestDataPath() {
return PluginTestCaseBase.getTestDataPathBase() + "/kdoc/rename/";
}
public void testParamReference() throws Exception {
doTest("bar");
}
public void testTypeParamReference() throws Exception {
doTest("R");
}
public void testCodeReference() throws Exception {
doTest("xyzzy");
}
@Override
protected void setUp() throws Exception {
super.setUp();
((StartupManagerImpl) StartupManager.getInstance(getProject())).runPostStartupActivities();
}
private void doTest(String newName) throws Exception {
configureByFile(getTestName(false) + ".kt");
PsiElement element = TargetElementUtil
.findTargetElement(getEditor(),
TargetElementUtil.ELEMENT_NAME_ACCEPTED | TargetElementUtil.REFERENCED_ELEMENT_ACCEPTED);
assertNotNull(element);
new RenameProcessor(getProject(), element, newName, true, true).run();
checkResultByFile(getTestName(false) + ".kt.after");
}
}
@@ -0,0 +1,31 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.navigation
import com.intellij.openapi.projectRoots.Sdk
import com.intellij.testFramework.LightCodeInsightTestCase
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
import org.jetbrains.kotlin.idea.test.invalidateLibraryCache
import org.jetbrains.kotlin.test.KotlinTestUtils
import java.io.File
abstract class AbstractKotlinGotoImplementationTest : LightCodeInsightTestCase() {
override fun setUp() {
super.setUp()
invalidateLibraryCache(project)
}
override fun getTestDataPath(): String = KotlinTestUtils.getHomeDirectory() + File.separator
override fun getProjectJDK(): Sdk = PluginTestCaseBase.mockJdk()
protected fun doTest(path: String) {
configureByFile(path)
val gotoData = NavigationTestUtils.invokeGotoImplementations(editor, file)
NavigationTestUtils.assertGotoDataMatching(editor, gotoData)
}
}
@@ -0,0 +1,256 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.refactoring
import com.intellij.codeInsight.TargetElementUtil
import com.intellij.codeInsight.template.impl.TemplateManagerImpl
import com.intellij.openapi.actionSystem.CommonDataKeys
import com.intellij.openapi.actionSystem.impl.SimpleDataContext
import com.intellij.openapi.command.WriteCommandAction
import com.intellij.refactoring.BaseRefactoringProcessor
import com.intellij.refactoring.rename.inplace.VariableInplaceRenameHandler
import com.intellij.testFramework.LightPlatformCodeInsightTestCase
import com.intellij.testFramework.fixtures.CodeInsightTestUtil
import junit.framework.TestCase
import org.jetbrains.kotlin.idea.refactoring.rename.KotlinMemberInplaceRenameHandler
import org.jetbrains.kotlin.idea.refactoring.rename.KotlinVariableInplaceRenameHandler
import org.jetbrains.kotlin.idea.refactoring.rename.RenameKotlinImplicitLambdaParameter
import org.jetbrains.kotlin.idea.refactoring.rename.findElementForRename
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
import org.jetbrains.kotlin.psi.KtNameReferenceExpression
import org.jetbrains.kotlin.test.InTextDirectivesUtils
import org.jetbrains.kotlin.test.JUnit3WithIdeaConfigurationRunner
import org.junit.runner.RunWith
import kotlin.test.assertFalse
import kotlin.test.assertTrue
@RunWith(JUnit3WithIdeaConfigurationRunner::class)
class InplaceRenameTest : LightPlatformCodeInsightTestCase() {
override fun isRunInWriteAction(): Boolean = false
override fun getTestDataPath(): String = PluginTestCaseBase.getTestDataPathBase() + "/refactoring/rename/inplace/"
fun testLocalVal() {
doTestMemberInplaceRename("y")
}
fun testForLoop() {
doTestInplaceRename("j")
}
fun testTryCatch() {
doTestInplaceRename("e1")
}
fun testFunctionLiteral() {
doTestInplaceRename("y")
}
fun testFunctionLiteralIt() {
doTestImplicitLambdaParameter("y")
}
fun testFunctionLiteralItEndCaret() {
doTestImplicitLambdaParameter("y")
}
fun testFunctionLiteralParenthesis() {
doTestInplaceRename("y")
}
fun testLocalFunction() {
doTestMemberInplaceRename("bar")
}
fun testFunctionParameterNotInplace() {
doTestInplaceRename(null)
}
fun testGlobalFunctionNotInplace() {
doTestInplaceRename(null)
}
fun testTopLevelValNotInplace() {
doTestInplaceRename(null)
}
fun testLabelFromFunction() {
doTestMemberInplaceRename("foo")
}
fun testMultiDeclaration() {
doTestInplaceRename("foo")
}
fun testLocalVarShadowingMemberProperty() {
doTestMemberInplaceRename("name1")
}
fun testNoReformat() {
doTestMemberInplaceRename("subject2")
}
fun testInvokeToFoo() {
doTestMemberInplaceRename("foo")
}
fun testInvokeToGet() {
doTestMemberInplaceRename("get")
}
fun testInvokeToGetWithQualifiedExpr() {
doTestMemberInplaceRename("get")
}
fun testInvokeToGetWithSafeQualifiedExpr() {
doTestMemberInplaceRename("get")
}
fun testInvokeToPlus() {
doTestMemberInplaceRename("plus")
}
fun testGetToFoo() {
doTestMemberInplaceRename("foo")
}
fun testGetToInvoke() {
doTestMemberInplaceRename("invoke")
}
fun testGetToInvokeWithQualifiedExpr() {
doTestMemberInplaceRename("invoke")
}
fun testGetToInvokeWithSafeQualifiedExpr() {
doTestMemberInplaceRename("invoke")
}
fun testGetToPlus() {
doTestMemberInplaceRename("plus")
}
fun testAddQuotes() {
doTestMemberInplaceRename("is")
}
fun testAddThis() {
doTestMemberInplaceRename("foo")
}
fun testExtensionAndNoReceiver() {
doTestMemberInplaceRename("b")
}
fun testTwoExtensions() {
doTestMemberInplaceRename("example")
}
fun testQuotedLocalVar() {
doTestMemberInplaceRename("x")
}
fun testQuotedParameter() {
doTestMemberInplaceRename("x")
}
fun testEraseCompanionName() {
doTestMemberInplaceRename("")
}
fun testLocalVarRedeclaration() {
doTestMemberInplaceRename("localValB")
}
fun testLocalFunRedeclaration() {
doTestMemberInplaceRename("localFunB")
}
fun testLocalClassRedeclaration() {
doTestMemberInplaceRename("LocalClassB")
}
fun testBacktickedWithAccessors() {
doTestMemberInplaceRename("`object`")
}
fun testNoTextUsagesForLocalVar() {
doTestMemberInplaceRename("w")
}
private fun doTestImplicitLambdaParameter(newName: String) {
configureByFile(getTestName(false) + ".kt")
// This code is copy-pasted from CodeInsightTestUtil.doInlineRename() and slightly modified.
// Original method was not suitable because it expects renamed element to be reference to other or referrable
val file = getFile()!!
val editor = getEditor()!!
val element = file.findElementForRename<KtNameReferenceExpression>(editor.caretModel.offset)!!
assertNotNull(element)
val dataContext = SimpleDataContext.getSimpleContext(CommonDataKeys.PSI_ELEMENT.name, element,
getCurrentEditorDataContext())
val handler = RenameKotlinImplicitLambdaParameter()
assertTrue(handler.isRenaming(dataContext), "In-place rename not allowed for " + element)
val project = editor.project!!
TemplateManagerImpl.setTemplateTesting(project, testRootDisposable)
object : WriteCommandAction.Simple<Any>(project) {
override fun run() {
handler.invoke(project, editor, file, dataContext)
}
}.execute()
var state = TemplateManagerImpl.getTemplateState(editor)
assert(state != null)
val range = state!!.currentVariableRange
assert(range != null)
object : WriteCommandAction.Simple<Any>(project) {
override fun run() {
editor.document.replaceString(range!!.startOffset, range.endOffset, newName)
}
}.execute().throwException()
state = TemplateManagerImpl.getTemplateState(editor)
assert(state != null)
state!!.gotoEnd(false)
checkResultByFile(getTestName(false) + ".kt.after")
}
private fun doTestMemberInplaceRename(newName: String?) {
doTestInplaceRename(newName, KotlinMemberInplaceRenameHandler())
}
private fun doTestInplaceRename(newName: String?, handler: VariableInplaceRenameHandler = KotlinVariableInplaceRenameHandler()) {
configureByFile(getTestName(false) + ".kt")
val element = TargetElementUtil.findTargetElement(
editor,
TargetElementUtil.ELEMENT_NAME_ACCEPTED or TargetElementUtil.REFERENCED_ELEMENT_ACCEPTED
)
assertNotNull(element)
val dataContext = SimpleDataContext.getSimpleContext(CommonDataKeys.PSI_ELEMENT.name, element!!, currentEditorDataContext)
if (newName == null) {
assertFalse(handler.isRenaming(dataContext), "In-place rename is allowed for " + element)
}
else {
try {
assertTrue(handler.isRenaming(dataContext), "In-place rename not allowed for " + element)
CodeInsightTestUtil.doInlineRename(handler, newName, getEditor(), element)
checkResultByFile(getTestName(false) + ".kt.after")
} catch (e: BaseRefactoringProcessor.ConflictsInTestsException) {
val expectedMessage = InTextDirectivesUtils.findStringWithPrefixes(file.text, "// SHOULD_FAIL_WITH: ")
TestCase.assertEquals(expectedMessage, e.messages.joinToString())
}
}
}
}
@@ -0,0 +1,60 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.refactoring.rename;
import com.intellij.codeInsight.TargetElementUtil;
import com.intellij.psi.PsiElement;
import com.intellij.refactoring.rename.RenameProcessor;
import com.intellij.testFramework.LightCodeInsightTestCase;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase;
public class SimpleNameReferenceRenameTest extends LightCodeInsightTestCase {
@NotNull
@Override
protected String getTestDataPath() {
return PluginTestCaseBase.getTestDataPathBase() + "/refactoring/rename/simpleNameReference/";
}
public void testRenameLabel() throws Exception {
doTest("foo");
}
public void testRenameLabel2() throws Exception {
doTest("anotherFoo");
}
public void testRenameField() throws Exception {
doTest("renamed");
}
public void testRenameFieldIdentifier() throws Exception {
doTest("anotherRenamed");
}
public void testMemberOfLocalObject() throws Exception {
doTest("bar");
}
public void testLocalFunction() throws Exception {
doTest("xyzzy");
}
public void testParameterOfCopyMethod() throws Exception {
doTest("y");
}
private void doTest(String newName) throws Exception {
configureByFile(getTestName(true) + ".kt");
PsiElement element = TargetElementUtil
.findTargetElement(getEditor(),
TargetElementUtil.ELEMENT_NAME_ACCEPTED | TargetElementUtil.REFERENCED_ELEMENT_ACCEPTED);
assertNotNull(element);
new RenameProcessor(getProject(), element, newName, true, true).run();
checkResultByFile(getTestName(true) + ".kt.after");
}
}
@@ -0,0 +1,311 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.script
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.extensions.Extensions
import com.intellij.openapi.module.JavaModuleType
import com.intellij.openapi.module.Module
import com.intellij.openapi.projectRoots.ProjectJdkTable
import com.intellij.openapi.roots.ModuleRootModificationUtil
import com.intellij.openapi.roots.ProjectRootManager
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.VfsUtil
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.testFramework.PlatformTestCase
import com.intellij.testFramework.PlatformTestUtil
import com.intellij.testFramework.PsiTestUtil
import com.intellij.util.ui.UIUtil
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime
import org.jetbrains.kotlin.idea.completion.test.KotlinCompletionTestCase
import org.jetbrains.kotlin.idea.core.script.IdeScriptReportSink
import org.jetbrains.kotlin.idea.core.script.ScriptConfigurationManager.Companion.updateScriptDependenciesSynchronously
import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionContributor
import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionsManager
import org.jetbrains.kotlin.idea.core.script.isScriptDependenciesUpdaterDisabled
import org.jetbrains.kotlin.idea.highlighter.KotlinHighlightingUtil
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition
import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.test.MockLibraryUtil
import org.jetbrains.kotlin.test.TestJdkKind
import org.jetbrains.kotlin.test.util.addDependency
import org.jetbrains.kotlin.test.util.projectLibrary
import org.jetbrains.kotlin.utils.PathUtil
import org.jetbrains.kotlin.utils.PathUtil.KOTLIN_JAVA_SCRIPT_RUNTIME_JAR
import org.jetbrains.kotlin.utils.PathUtil.KOTLIN_SCRIPTING_COMMON_JAR
import org.jetbrains.kotlin.utils.PathUtil.KOTLIN_SCRIPTING_JVM_JAR
import java.io.File
import java.util.regex.Pattern
import kotlin.script.dependencies.Environment
import kotlin.script.experimental.api.ScriptDiagnostic
private val validKeys = setOf("javaHome", "sources", "classpath", "imports", "template-classes-names")
private const val useDefaultTemplate = "// DEPENDENCIES:"
private const val templatesSettings = "// TEMPLATES: "
// some bugs can only be reproduced when some module and script have intersecting library dependencies
private const val configureConflictingModule = "// CONFLICTING_MODULE"
private fun String.splitOrEmpty(delimeters: String) = split(delimeters).takeIf { it.size > 1 } ?: emptyList()
internal val switches = listOf(
useDefaultTemplate,
configureConflictingModule
)
abstract class AbstractScriptConfigurationTest : KotlinCompletionTestCase() {
companion object {
private const val SCRIPT_NAME = "script.kts"
}
override fun setUpModule() {
// do not create default module
}
private fun findMainScript(testDir: String): File {
val scriptFile = File(testDir).walkTopDown().find { it.name == SCRIPT_NAME }
if (scriptFile != null) return scriptFile
return File(testDir).walkTopDown().singleOrNull { it.name.contains("script") }
?: error("Couldn't find $SCRIPT_NAME file in $testDir")
}
private val sdk by lazy {
val jdk = PluginTestCaseBase.jdk(TestJdkKind.MOCK_JDK)
runWriteAction {
ProjectJdkTable.getInstance().addJdk(jdk, testRootDisposable)
ProjectRootManager.getInstance(project).projectSdk = jdk
}
jdk
}
protected fun configureScriptFile(path: String) {
val mainScriptFile = findMainScript(path)
val environment = createScriptEnvironment(mainScriptFile)
registerScriptTemplateProvider(environment)
File(path, "mainModule").takeIf { it.exists() }?.let {
myModule = createTestModuleFromDir(it)
}
File(path).listFiles { file -> file.name.startsWith("module") }.filter { it.exists() }.forEach {
val newModule = createTestModuleFromDir(it)
assert(myModule != null) { "Main module should exists" }
ModuleRootModificationUtil.addDependency(myModule, newModule)
}
if (module != null) {
module.addDependency(
projectLibrary(
"script-runtime",
classesRoot = VfsUtil.findFileByIoFile(PathUtil.kotlinPathsForDistDirectory.scriptRuntimePath, true)
)
)
if (environment["template-classes"] != null) {
module.addDependency(
projectLibrary(
"script-template-library",
classesRoot = VfsUtil.findFileByIoFile(environment["template-classes"] as File, true)
)
)
}
}
if (configureConflictingModule in environment) {
val sharedLib = VfsUtil.findFileByIoFile(environment["lib-classes"] as File, true)!!
if (module == null) {
// Force create module if it doesn't exist
myModule = createTestModuleByName("mainModule")
}
module.addDependency(projectLibrary("sharedLib", classesRoot = sharedLib))
}
if (module != null) {
ModuleRootModificationUtil.updateModel(module) { model ->
model.sdk = sdk
}
}
createFileAndSyncDependencies(mainScriptFile)
}
private val oldScripClasspath: String? = System.getProperty("kotlin.script.classpath")
override fun setUp() {
super.setUp()
ApplicationManager.getApplication().isScriptDependenciesUpdaterDisabled = true
}
override fun tearDown() {
ApplicationManager.getApplication().isScriptDependenciesUpdaterDisabled = false
System.setProperty("kotlin.script.classpath", oldScripClasspath ?: "")
super.tearDown()
}
private fun createTestModuleByName(name: String): Module {
val newModuleDir = runWriteAction { VfsUtil.createDirectoryIfMissing(project.baseDir, name) }
val newModule = createModuleAt(name, project, JavaModuleType.getModuleType(), newModuleDir.path)
// Return type was changed, but it's not used. BUNCH: 183
@Suppress("MissingRecentApi")
PsiTestUtil.addSourceContentToRoots(newModule, newModuleDir)
return newModule
}
private fun createTestModuleFromDir(dir: File): Module {
return createTestModuleByName(dir.name).apply {
PlatformTestCase.copyDirContentsTo(LocalFileSystem.getInstance().findFileByIoFile(dir)!!, moduleFile!!.parent)
}
}
private fun createScriptEnvironment(scriptFile: File): Environment {
val defaultEnvironment = defaultEnvironment(scriptFile.parent + File.separator)
val env = mutableMapOf<String, Any?>()
scriptFile.forEachLine { line ->
fun iterateKeysInLine(prefix: String) {
if (line.contains(prefix)) {
line.trim().substringAfter(prefix).split(";").forEach { entry ->
val (key, values) = entry.splitOrEmpty(":").map { it.trim() }
assert(key in validKeys) { "Unexpected key: $key" }
env[key] = values.split(",").map {
val str = it.trim()
defaultEnvironment[str] ?: str
}
}
}
}
iterateKeysInLine(useDefaultTemplate)
iterateKeysInLine(templatesSettings)
switches.forEach {
if (it in line) {
env[it] = true
}
}
}
if (env[useDefaultTemplate] != true && env["template-classes-names"] == null) {
env["template-classes-names"] = listOf("custom.scriptDefinition.Template")
}
if (env["javaHome"] != null) {
val jdkKind = when ((env["javaHome"] as? List<String>)?.singleOrNull()) {
"9" -> TestJdkKind.FULL_JDK_9
else -> TestJdkKind.MOCK_JDK
}
runWriteAction {
val jdk = PluginTestCaseBase.jdk(jdkKind)
ProjectJdkTable.getInstance().addJdk(jdk, testRootDisposable)
env["javaHome"] = File(jdk.homePath)
}
}
env.putAll(defaultEnvironment)
return env
}
private fun defaultEnvironment(path: String): Map<String, File?> {
val templateOutDir = File("${path}template").takeIf { it.isDirectory }?.let {
compileLibToDir(it, *scriptClasspath())
} ?: File("idea/testData/script/definition/defaultTemplate").takeIf { it.isDirectory }?.let {
compileLibToDir(it, *scriptClasspath())
}
val libSrcDir = File("${path}lib").takeIf { it.isDirectory }
val libClasses = libSrcDir?.let { compileLibToDir(it) }
return mapOf(
"runtime-classes" to ForTestCompileRuntime.runtimeJarForTests(),
"runtime-source" to File("libraries/stdlib/src"),
"lib-classes" to libClasses,
"lib-source" to libSrcDir,
"template-classes" to templateOutDir
)
}
private fun scriptClasspath(): Array<String> {
return with(PathUtil.kotlinPathsForDistDirectory) {
arrayOf(
File(libPath, KOTLIN_JAVA_SCRIPT_RUNTIME_JAR).path,
File(libPath, KOTLIN_SCRIPTING_COMMON_JAR).path,
File(libPath, KOTLIN_SCRIPTING_JVM_JAR).path
)
}
}
private fun createFileAndSyncDependencies(scriptFile: File) {
var script: VirtualFile? = null
if (module != null) {
script = module.moduleFile?.parent?.findChild(scriptFile.name)
}
if (script == null) {
val target = File(project.basePath, scriptFile.name)
scriptFile.copyTo(target)
script = VfsUtil.findFileByIoFile(target, true)
}
if (script == null) error("Test file with script couldn't be found in test project")
configureByExistingFile(script)
updateScriptDependenciesSynchronously(myFile, project)
VfsUtil.markDirtyAndRefresh(false, true, true, project.baseDir)
// This is needed because updateScriptDependencies invalidates psiFile that was stored in myFile field
myFile = psiManager.findFile(script)
val ktFile = myFile as KtFile
val reports = IdeScriptReportSink.getReports(ktFile)
val isFatalErrorPresent = reports.any { it.severity == ScriptDiagnostic.Severity.FATAL }
assert(isFatalErrorPresent || KotlinHighlightingUtil.shouldHighlight(myFile)) {
"Highlighting is switched off for ${myFile.virtualFile.path}\n" +
"reports=$reports\n" +
"scriptDefinition=${ktFile.findScriptDefinition()}"
}
}
private fun compileLibToDir(srcDir: File, vararg classpath: String): File {
//TODO: tmpDir would be enough, but there is tricky fail under AS otherwise
val outDir = KotlinTestUtils.tmpDirForReusableFolder("${getTestName(false)}${srcDir.name}Out")
val kotlinSourceFiles = FileUtil.findFilesByMask(Pattern.compile(".+\\.kt$"), srcDir)
if (kotlinSourceFiles.isNotEmpty()) {
MockLibraryUtil.compileKotlin(srcDir.path, outDir, extraClasspath = *classpath)
}
val javaSourceFiles = FileUtil.findFilesByMask(Pattern.compile(".+\\.java$"), srcDir)
if (javaSourceFiles.isNotEmpty()) {
KotlinTestUtils.compileJavaFiles(
javaSourceFiles,
listOf("-cp", StringUtil.join(listOf(*classpath, outDir), File.pathSeparator), "-d", outDir.path)
)
}
return outDir
}
private fun registerScriptTemplateProvider(environment: Environment) {
val provider = if (environment[useDefaultTemplate] == true) {
FromTextTemplateProvider(environment)
} else {
CustomScriptTemplateProvider(environment)
}
ScriptDefinitionContributor.EP_NAME.getPoint(null).registerExtension(provider)
ScriptDefinitionsManager.getInstance(project).reloadScriptDefinitions()
UIUtil.dispatchAllInvocationEvents()
}
}
@@ -0,0 +1,54 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.test.util
import com.intellij.openapi.module.Module
import com.intellij.openapi.roots.DependencyScope
import com.intellij.openapi.roots.ModuleRootModificationUtil
import com.intellij.openapi.roots.OrderRootType
import com.intellij.openapi.roots.impl.libraries.ProjectLibraryTable
import com.intellij.openapi.roots.libraries.Library
import com.intellij.openapi.roots.libraries.PersistentLibraryKind
import com.intellij.openapi.vfs.JarFileSystem
import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.testFramework.HeavyPlatformTestCase
import org.jetbrains.kotlin.test.testFramework.runWriteAction
import java.io.File
fun HeavyPlatformTestCase.projectLibrary(
libraryName: String = "TestLibrary",
classesRoot: VirtualFile? = null,
sourcesRoot: VirtualFile? = null,
kind: PersistentLibraryKind<*>? = null
): Library {
return runWriteAction {
val modifiableModel = ProjectLibraryTable.getInstance(project).modifiableModel
val library = try {
modifiableModel.createLibrary(libraryName, kind)
} finally {
modifiableModel.commit()
}
with (library.modifiableModel) {
classesRoot?.let { addRoot(it, OrderRootType.CLASSES) }
sourcesRoot?.let { addRoot(it, OrderRootType.SOURCES) }
commit()
}
library
}
}
val File.jarRoot: VirtualFile
get() {
val virtualFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(this) ?: error("Cannot find file $this")
return JarFileSystem.getInstance().getRootByLocal(virtualFile) ?: error("Can't find root by file $virtualFile")
}
fun Module.addDependency(
library: Library,
dependencyScope: DependencyScope = DependencyScope.COMPILE,
exported: Boolean = false
) = ModuleRootModificationUtil.addDependency(this, library, dependencyScope, exported)
@@ -0,0 +1,136 @@
/*
* Copyright 2010-2015 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 org.jetbrains.kotlin.j2k
import com.intellij.codeInsight.ContainerProvider
import com.intellij.codeInsight.NullableNotNullManager
import com.intellij.codeInsight.runner.JavaMainMethodProvider
import com.intellij.core.CoreApplicationEnvironment
import com.intellij.core.JavaCoreApplicationEnvironment
import com.intellij.core.JavaCoreProjectEnvironment
import com.intellij.lang.MetaLanguage
import com.intellij.lang.jvm.facade.JvmElementProvider
import com.intellij.openapi.extensions.Extensions
import com.intellij.openapi.extensions.ExtensionsArea
import com.intellij.openapi.fileTypes.FileTypeExtensionPoint
import com.intellij.openapi.util.Disposer
import com.intellij.openapi.util.io.FileUtil
import com.intellij.psi.*
import com.intellij.psi.augment.PsiAugmentProvider
import com.intellij.psi.augment.TypeAnnotationModifier
import com.intellij.psi.compiled.ClassFileDecompilers
import com.intellij.psi.impl.JavaClassSupersImpl
import com.intellij.psi.impl.PsiTreeChangePreprocessor
import com.intellij.psi.meta.MetaDataContributor
import com.intellij.psi.stubs.BinaryFileStubBuilders
import com.intellij.psi.util.JavaClassSupers
import junit.framework.TestCase
import org.jetbrains.kotlin.utils.PathUtil
import java.io.File
import java.net.URLClassLoader
abstract class AbstractJavaToKotlinConverterForWebDemoTest : TestCase() {
val DISPOSABLE = Disposer.newDisposable()
fun doTest(javaPath: String) {
try {
val fileContents = FileUtil.loadFile(File(javaPath), true)
val javaCoreEnvironment: JavaCoreProjectEnvironment = setUpJavaCoreEnvironment()
translateToKotlin(fileContents, javaCoreEnvironment.project)
}
finally {
Disposer.dispose(DISPOSABLE)
}
}
fun setUpJavaCoreEnvironment(): JavaCoreProjectEnvironment {
// FIXME: There is no `Extensions.cleanRootArea` in 193 platform
// Extensions.cleanRootArea(DISPOSABLE)
val area = Extensions.getRootArea()
registerExtensionPoints(area)
val applicationEnvironment = JavaCoreApplicationEnvironment(DISPOSABLE)
val javaCoreEnvironment = object : JavaCoreProjectEnvironment(DISPOSABLE, applicationEnvironment) {
override fun preregisterServices() {
val projectArea = Extensions.getArea(project)
CoreApplicationEnvironment.registerExtensionPoint(projectArea, PsiTreeChangePreprocessor.EP_NAME, PsiTreeChangePreprocessor::class.java)
CoreApplicationEnvironment.registerExtensionPoint(projectArea, PsiElementFinder.EP_NAME, PsiElementFinder::class.java)
CoreApplicationEnvironment.registerExtensionPoint(projectArea, JvmElementProvider.EP_NAME, JvmElementProvider::class.java)
}
}
javaCoreEnvironment.project.registerService(NullableNotNullManager::class.java, object : NullableNotNullManager(javaCoreEnvironment.project) {
override fun isNullable(owner: PsiModifierListOwner, checkBases: Boolean) = !isNotNull(owner, checkBases)
override fun isNotNull(owner: PsiModifierListOwner, checkBases: Boolean) = true
override fun hasHardcodedContracts(element: PsiElement): Boolean = false
override fun getNullables() = emptyList<String>()
override fun setNullables(vararg p0: String) = Unit
override fun getNotNulls() = emptyList<String>()
override fun setNotNulls(vararg p0: String) = Unit
override fun getDefaultNullable() = ""
override fun setDefaultNullable(defaultNullable: String) = Unit
override fun getDefaultNotNull() = ""
override fun setDefaultNotNull(p0: String) = Unit
override fun setInstrumentedNotNulls(p0: List<String>) = Unit
override fun getInstrumentedNotNulls() = emptyList<String>()
})
applicationEnvironment.application.registerService(JavaClassSupers::class.java, JavaClassSupersImpl::class.java)
for (root in PathUtil.getJdkClassesRootsFromCurrentJre()) {
javaCoreEnvironment.addJarToClassPath(root)
}
val annotations: File? = findAnnotations()
if (annotations != null && annotations.exists()) {
javaCoreEnvironment.addJarToClassPath(annotations)
}
return javaCoreEnvironment
}
private fun registerExtensionPoints(area: ExtensionsArea) {
CoreApplicationEnvironment.registerExtensionPoint(area, BinaryFileStubBuilders.EP_NAME, FileTypeExtensionPoint::class.java)
CoreApplicationEnvironment.registerExtensionPoint(area, FileContextProvider.EP_NAME, FileContextProvider::class.java)
CoreApplicationEnvironment.registerExtensionPoint(area, MetaDataContributor.EP_NAME, MetaDataContributor::class.java)
CoreApplicationEnvironment.registerExtensionPoint(area, PsiAugmentProvider.EP_NAME, PsiAugmentProvider::class.java)
CoreApplicationEnvironment.registerExtensionPoint(area, JavaMainMethodProvider.EP_NAME, JavaMainMethodProvider::class.java)
CoreApplicationEnvironment.registerExtensionPoint(area, ContainerProvider.EP_NAME, ContainerProvider::class.java)
CoreApplicationEnvironment.registerExtensionPoint(area, ClassFileDecompilers.EP_NAME, ClassFileDecompilers.Decompiler::class.java)
CoreApplicationEnvironment.registerExtensionPoint(area, TypeAnnotationModifier.EP_NAME, TypeAnnotationModifier::class.java)
CoreApplicationEnvironment.registerExtensionPoint(area, MetaLanguage.EP_NAME, MetaLanguage::class.java)
CoreApplicationEnvironment.registerExtensionPoint(area, JavaModuleSystem.EP_NAME, JavaModuleSystem::class.java)
}
fun findAnnotations(): File? {
var classLoader = JavaToKotlinTranslator::class.java.classLoader
while (classLoader != null) {
val loader = classLoader
if (loader is URLClassLoader) {
for (url in loader.urLs) {
if ("file" == url.protocol && url.file!!.endsWith("/annotations.jar")) {
return File(url.file!!)
}
}
}
classLoader = classLoader.parent
}
return null
}
}
@@ -0,0 +1,127 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.uast.test.kotlin
import com.intellij.openapi.Disposable
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.extensions.Extensions
import com.intellij.openapi.util.Disposer
import com.intellij.patterns.uast.injectionHostUExpression
import com.intellij.psi.*
import com.intellij.psi.impl.source.resolve.reference.PsiReferenceContributorEP
import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry
import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistryImpl
import com.intellij.psi.util.PropertyUtil
import com.intellij.testFramework.LightProjectDescriptor
import com.intellij.testFramework.registerServiceInstance
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor
import org.jetbrains.kotlin.test.JUnit3WithIdeaConfigurationRunner
import org.jetbrains.uast.UExpression
import org.jetbrains.uast.evaluateString
import org.jetbrains.uast.toUElementOfType
import org.junit.Test
import org.junit.runner.RunWith
import kotlin.test.fail
@RunWith(JUnit3WithIdeaConfigurationRunner::class)
class KotlinUastReferencesTest : KotlinLightCodeInsightFixtureTestCase() {
override fun getProjectDescriptor(): LightProjectDescriptor = KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE
@Test
fun `test original getter is visible when reference is under renaming`() {
registerReferenceProviders(testRootDisposable) {
registerUastReferenceProvider(injectionHostUExpression(), uastInjectionHostReferenceProvider { _, psiLanguageInjectionHost ->
arrayOf(GetterReference("KotlinBean", psiLanguageInjectionHost))
})
}
myFixture.configureByText(
"KotlinBean.kt", """
data class KotlinBean(val myF<caret>ield: String)
val reference = "myField"
""".trimIndent()
)
myFixture.renameElementAtCaret("myRenamedField")
myFixture.checkResult(
"""
data class KotlinBean(val myRenamedField: String)
val reference = "myRenamedField"
""".trimIndent()
)
}
}
private class GetterReference(
val className: String,
psiElement: PsiElement
) : PsiReferenceBase<PsiElement>(psiElement) {
override fun resolve(): PsiMethod? {
val psiClass = JavaPsiFacade.getInstance(element.project).findClass(className, element.resolveScope) ?: return null
val name = element.toUElementOfType<UExpression>()?.evaluateString() ?: return null
return PropertyUtil.getGetters(psiClass, name).firstOrNull()
}
override fun handleElementRename(newElementName: String): PsiElement {
val resolve = resolve()
?: fail("can't resolve during rename, looks like someone renamed or removed the source element before updating references")
val newName =
if (PropertyUtil.getPropertyName(resolve) != null)
PropertyUtil.getPropertyName(newElementName) ?: newElementName
else newElementName
return super.handleElementRename(newName)
}
override fun getVariants(): Array<Any> = emptyArray()
}
fun registerReferenceProviders(disposable: Disposable, registerContributors: PsiReferenceRegistrar.() -> Unit) {
registerReferenceContributor(object : PsiReferenceContributor() {
override fun registerReferenceProviders(registrar: PsiReferenceRegistrar) = registrar.registerContributors()
}, disposable)
}
fun registerReferenceContributor(contributor: PsiReferenceContributor, disposable: Disposable) {
error("PsiReferenceContributorEP is final in 193 platform")
// val referenceContributorEp = Extensions.getArea(null).getExtensionPoint<PsiReferenceContributorEP>(PsiReferenceContributor.EP_NAME.name)
//
// val contributorEp = object : PsiReferenceContributorEP() {
// override fun getInstance(): PsiReferenceContributor = contributor
// }
//
// referenceContributorEp.registerExtension(contributorEp)
//
// val application = ApplicationManager.getApplication()
//
// //we need a fresh ReferenceProvidersRegistry after updating ReferenceContributors
// val oldReferenceProviderRegistry =
// application.picoContainer.getComponentInstance(ReferenceProvidersRegistry::class.java) as ReferenceProvidersRegistry
// application.registerServiceInstance(ReferenceProvidersRegistry::class.java, ReferenceProvidersRegistryImpl())
//
// Disposer.register(disposable, Disposable {
// referenceContributorEp.unregisterExtension(contributorEp)
// application.registerServiceInstance(ReferenceProvidersRegistry::class.java, oldReferenceProviderRegistry)
// })
}