From d54bc3a4bb979c084d283405697c975b6b3667ea Mon Sep 17 00:00:00 2001 From: Vyacheslav Gerasimov Date: Sat, 21 Sep 2019 15:36:13 +0400 Subject: [PATCH] 193: Fix tests compilation for 193 platform --- .../testFramework/KtParsingTestCase.java.193 | 332 +++++++ .../KtPlatformLiteFixture.java.193 | 81 ++ .../confidence/KotlinConfidenceTest.java.193 | 179 ++++ .../gradle/AbstractModelBuilderTest.java.193 | 244 +++++ .../AbstractPerformanceProjectsTest.kt.193 | 551 +++++++++++ .../idea/testFramework/projectRoutines.kt.193 | 94 ++ .../formatter/AbstractFormatterTest.java.193 | 146 +++ ...AbstractTypingIndentationTestBase.java.193 | 68 ++ .../idea/actions/QualifiedNamesTest.kt.193 | 79 ++ .../moveUpDown/AbstractCodeMoverTest.kt.193 | 127 +++ .../KotlinPsiChainBuilderTestCase.kt.193 | 75 ++ .../idea/editor/KotlinCommenterTest.java.193 | 86 ++ .../idea/editor/TypedHandlerTest.kt.193 | 935 ++++++++++++++++++ .../kotlin/idea/kdoc/KdocRenameTest.java.193 | 54 + ...bstractKotlinGotoImplementationTest.kt.193 | 31 + .../idea/refactoring/InplaceRenameTest.kt.193 | 256 +++++ .../SimpleNameReferenceRenameTest.java.193 | 60 ++ .../AbstractScriptConfigurationTest.kt.193 | 311 ++++++ .../test/util/ProjectStructureUtils.kt.193 | 54 + ...JavaToKotlinConverterForWebDemoTest.kt.193 | 136 +++ .../tests/KotlinUastReferencesTest.kt.193 | 127 +++ 21 files changed, 4026 insertions(+) create mode 100644 compiler/tests-common/tests/org/jetbrains/kotlin/test/testFramework/KtParsingTestCase.java.193 create mode 100644 compiler/tests-common/tests/org/jetbrains/kotlin/test/testFramework/KtPlatformLiteFixture.java.193 create mode 100644 idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/confidence/KotlinConfidenceTest.java.193 create mode 100644 idea/idea-gradle/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/AbstractModelBuilderTest.java.193 create mode 100644 idea/performanceTests/org/jetbrains/kotlin/idea/perf/AbstractPerformanceProjectsTest.kt.193 create mode 100644 idea/performanceTests/org/jetbrains/kotlin/idea/testFramework/projectRoutines.kt.193 create mode 100644 idea/tests/org/jetbrains/kotlin/formatter/AbstractFormatterTest.java.193 create mode 100644 idea/tests/org/jetbrains/kotlin/formatter/AbstractTypingIndentationTestBase.java.193 create mode 100644 idea/tests/org/jetbrains/kotlin/idea/actions/QualifiedNamesTest.kt.193 create mode 100644 idea/tests/org/jetbrains/kotlin/idea/codeInsight/moveUpDown/AbstractCodeMoverTest.kt.193 create mode 100644 idea/tests/org/jetbrains/kotlin/idea/debugger/sequence/KotlinPsiChainBuilderTestCase.kt.193 create mode 100644 idea/tests/org/jetbrains/kotlin/idea/editor/KotlinCommenterTest.java.193 create mode 100644 idea/tests/org/jetbrains/kotlin/idea/editor/TypedHandlerTest.kt.193 create mode 100644 idea/tests/org/jetbrains/kotlin/idea/kdoc/KdocRenameTest.java.193 create mode 100644 idea/tests/org/jetbrains/kotlin/idea/navigation/AbstractKotlinGotoImplementationTest.kt.193 create mode 100644 idea/tests/org/jetbrains/kotlin/idea/refactoring/InplaceRenameTest.kt.193 create mode 100644 idea/tests/org/jetbrains/kotlin/idea/refactoring/rename/SimpleNameReferenceRenameTest.java.193 create mode 100644 idea/tests/org/jetbrains/kotlin/idea/script/AbstractScriptConfigurationTest.kt.193 create mode 100644 idea/tests/org/jetbrains/kotlin/test/util/ProjectStructureUtils.kt.193 create mode 100644 j2k/tests/org/jetbrains/kotlin/j2k/AbstractJavaToKotlinConverterForWebDemoTest.kt.193 create mode 100644 plugins/uast-kotlin/tests/KotlinUastReferencesTest.kt.193 diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/test/testFramework/KtParsingTestCase.java.193 b/compiler/tests-common/tests/org/jetbrains/kotlin/test/testFramework/KtParsingTestCase.java.193 new file mode 100644 index 00000000000..2b293989b60 --- /dev/null +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/test/testFramework/KtParsingTestCase.java.193 @@ -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 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() { + @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 void addExplicitExtension(final LanguageExtension 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 void registerExtensionPoint(final ExtensionPointName extensionPointName, Class aClass) { + super.registerExtensionPoint(extensionPointName, aClass); + Disposer.register(myProject, new Disposable() { + @Override + public void dispose() { + Extensions.getRootArea().unregisterExtensionPoint(extensionPointName.getName()); + } + }); + } + + protected void registerApplicationService(final Class 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 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)); + } +} \ No newline at end of file diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/test/testFramework/KtPlatformLiteFixture.java.193 b/compiler/tests-common/tests/org/jetbrains/kotlin/test/testFramework/KtPlatformLiteFixture.java.193 new file mode 100644 index 00000000000..918542f7249 --- /dev/null +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/test/testFramework/KtPlatformLiteFixture.java.193 @@ -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 void registerExtensionPoint(ExtensionPointName extensionPointName, Class aClass) { + registerExtensionPoint(Extensions.getRootArea(), extensionPointName, aClass); + } + + private static void registerExtensionPoint( + ExtensionsArea area, ExtensionPointName extensionPointName, + Class 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 registerComponentInstance(MutablePicoContainer container, Class key, T implementation) { + Object old = container.getComponentInstance(key); + container.unregisterComponent(key); + container.registerComponentInstance(key, implementation); + return (T)old; + } +} diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/confidence/KotlinConfidenceTest.java.193 b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/confidence/KotlinConfidenceTest.java.193 new file mode 100644 index 00000000000..44e25c2260b --- /dev/null +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/confidence/KotlinConfidenceTest.java.193 @@ -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 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 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)); + } +} diff --git a/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/AbstractModelBuilderTest.java.193 b/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/AbstractModelBuilderTest.java.193 new file mode 100644 index 00000000000..dfcd08752b5 --- /dev/null +++ b/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/AbstractModelBuilderTest.java.193 @@ -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 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 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 getToolingExtensionClasses() { + Set classes = ContainerUtil.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 doGetToolingExtensionClasses() { + return Collections.emptySet(); + } + + @After + public void tearDown() throws Exception { + if (testDir != null) { + FileUtil.delete(testDir); + } + } + + protected abstract Set 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; + } + } +} diff --git a/idea/performanceTests/org/jetbrains/kotlin/idea/perf/AbstractPerformanceProjectsTest.kt.193 b/idea/performanceTests/org/jetbrains/kotlin/idea/perf/AbstractPerformanceProjectsTest.kt.193 new file mode 100644 index 00000000000..857894967fa --- /dev/null +++ b/idea/performanceTests/org/jetbrains/kotlin/idea/perf/AbstractPerformanceProjectsTest.kt.193 @@ -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( + 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, + 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, + typeAfterMarker: Boolean = true, + revertChangesAtTheEnd: Boolean = true, + note: String = "" + ) { + assertTrue("lookupElements has to be not empty", lookupElements.isNotEmpty()) + stats.perfTest, Array>( + 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, List>( + 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 = + perfHighlightFile(myProject!!, name, stats) + + protected fun perfHighlightFile( + project: Project, + fileName: String, + stats: Stats, + note: String = "" + ): List { + return highlightFile { + val isWarmUp = note == WARM_UP + var highlightInfos: List = emptyList() + stats.perfTest>( + 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 highlightFile(block: () -> T): T { + var value: T? = null + IdentifierHighlighterPassFactory.doWithHighlightingEnabled { + value = block() + } + return value!! + } + + private fun highlightFile(project: Project, psiFile: PsiFile): List { + 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( + 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 "" + + +} \ No newline at end of file diff --git a/idea/performanceTests/org/jetbrains/kotlin/idea/testFramework/projectRoutines.kt.193 b/idea/performanceTests/org/jetbrains/kotlin/idea/testFramework/projectRoutines.kt.193 new file mode 100644 index 00000000000..04379026723 --- /dev/null +++ b/idea/performanceTests/org/jetbrains/kotlin/idea/testFramework/projectRoutines.kt.193 @@ -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>(LanguageAnnotators.EP_NAME) + val extensionPoint = pointName.getPoint(null) + + val point = LanguageExtensionPoint() + 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()}") +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/formatter/AbstractFormatterTest.java.193 b/idea/tests/org/jetbrains/kotlin/formatter/AbstractFormatterTest.java.193 new file mode 100644 index 00000000000..3aeee21e14d --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/formatter/AbstractFormatterTest.java.193 @@ -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 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(); + } + } +} diff --git a/idea/tests/org/jetbrains/kotlin/formatter/AbstractTypingIndentationTestBase.java.193 b/idea/tests/org/jetbrains/kotlin/formatter/AbstractTypingIndentationTestBase.java.193 new file mode 100644 index 00000000000..7b19a91fd73 --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/formatter/AbstractTypingIndentationTestBase.java.193 @@ -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 ""; + } +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/actions/QualifiedNamesTest.kt.193 b/idea/tests/org/jetbrains/kotlin/idea/actions/QualifiedNamesTest.kt.193 new file mode 100644 index 00000000000..d12eb5d3eac --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/actions/QualifiedNamesTest.kt.193 @@ -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 { + val result = ArrayList() + 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 + } +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/moveUpDown/AbstractCodeMoverTest.kt.193 b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/moveUpDown/AbstractCodeMoverTest.kt.193 new file mode 100644 index 00000000000..ebda6197efb --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/moveUpDown/AbstractCodeMoverTest.kt.193 @@ -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) { + 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() = "" +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/debugger/sequence/KotlinPsiChainBuilderTestCase.kt.193 b/idea/tests/org/jetbrains/kotlin/idea/debugger/sequence/KotlinPsiChainBuilderTestCase.kt.193 new file mode 100644 index 00000000000..6e90195b119 --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/debugger/sequence/KotlinPsiChainBuilderTestCase.kt.193 @@ -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) { + 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()) + } + } +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/editor/KotlinCommenterTest.java.193 b/idea/tests/org/jetbrains/kotlin/idea/editor/KotlinCommenterTest.java.193 new file mode 100644 index 00000000000..684452e953d --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/editor/KotlinCommenterTest.java.193 @@ -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); + } +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/editor/TypedHandlerTest.kt.193 b/idea/tests/org/jetbrains/kotlin/idea/editor/TypedHandlerTest.kt.193 new file mode 100644 index 00000000000..53e282be48c --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/editor/TypedHandlerTest.kt.193 @@ -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 = "$" """, + """val x = "$dollar{}" """ + ) + + fun testAutoIndentRightOpenBrace() = doTypeTest( + '{', + + "fun test() {\n" + + "\n" + + "}", + + "fun test() {\n" + + " {}\n" + + "}" + ) + + fun testAutoIndentLeftOpenBrace() = doTypeTest( + '{', + + "fun test() {\n" + + " \n" + + "}", + + "fun test() {\n" + + " {}\n" + + "}" + ) + + fun testTypeStringTemplateStartWithCloseBraceAfter() = doTypeTest( + '{', + """fun foo() { "$" }""", + """fun foo() { "$dollar{}" }""" + ) + + fun testTypeStringTemplateStartBeforeStringWithExistingDollar() = doTypeTest( + '{', + """fun foo() { "$something" }""", + """fun foo() { "$dollar{something" }""" + ) + + fun testTypeStringTemplateStartBeforeStringWithNoDollar() = doTypeTest( + "$dollar{", + """fun foo() { "something" }""", + """fun foo() { "$dollar{}something" }""" + ) + + fun testTypeStringTemplateWithUnmatchedBrace() = doTypeTest( + "$dollar{", + """val a = "bar}foo"""", + """val a = "$dollar{bar}foo"""" + ) + + fun testTypeStringTemplateWithUnmatchedBraceComplex() = doTypeTest( + "$dollar{", + """val a = "bar + more}foo"""", + """val a = "$dollar{}bar + more}foo"""" + ) + + fun testTypeStringTemplateStartInStringWithBraceLiterals() = doTypeTest( + "$dollar{", + """val test = "{ code other }"""", + """val test = "{ code $dollar{}other }"""" + ) + + fun testTypeStringTemplateStartInEmptyString() = doTypeTest( + '{', + """fun foo() { "$" }""", + """fun foo() { "$dollar{}" }""" + ) + + fun testKT3575() = doTypeTest( + '{', + """val x = "$]" """, + """val x = "$dollar{}]" """ + ) + + fun testAutoCloseRawStringInEnd() = doTypeTest( + '"', + """val x = """"", + """val x = ""${'"'}""${'"'}""" + ) + + fun testNoAutoCloseRawStringInEnd() = doTypeTest( + '"', + """val x = ""${'"'}""", + """val x = ""${'"'}"""" + ) + + fun testAutoCloseRawStringInMiddle() = doTypeTest( + '"', + """ + val x = "" + val y = 12 + """.trimIndent(), + """ + val x = ""${'"'}""${'"'} + val y = 12 + """.trimIndent() + ) + + fun testNoAutoCloseBetweenMultiQuotes() = doTypeTest( + '"', + """val x = ""${'"'}${'"'}""/**/""", + """val x = ""${'"'}${'"'}""/**/""" + ) + + fun testNoAutoCloseBetweenMultiQuotes1() = doTypeTest( + '"', + """val x = ""${'"'}""${'"'}/**/""", + """val x = ""${'"'}""${'"'}/**/""" + ) + + fun testNoAutoCloseAfterEscape() = doTypeTest( + '"', + """val x = "\""""", + """val x = "\""${'"'}"""" + ) + + fun testAutoCloseBraceInFunctionDeclaration() = doTypeTest( + '{', + "fun foo() ", + "fun foo() {}" + ) + + fun testAutoCloseBraceInLocalFunctionDeclaration() = doTypeTest( + '{', + + "fun foo() {\n" + + " fun bar() \n" + + "}", + + "fun foo() {\n" + + " fun bar() {}\n" + + "}" + ) + + fun testAutoCloseBraceInAssignment() = doTypeTest( + '{', + "fun foo() {\n" + + " val a = \n" + + "}", + + "fun foo() {\n" + + " val a = {}\n" + + "}" + ) + + fun testDoNotAutoCloseBraceInUnfinishedIfSurroundOnSameLine() = doTypeTest( + '{', + + "fun foo() {\n" + + " if() foo()\n" + + "}", + + "fun foo() {\n" + + " if() {foo()\n" + + "}" + ) + + fun testDoNotAutoCloseBraceInUnfinishedElseSurroundOnSameLine() = doTypeTest( + '{', + + "fun foo() {\n" + + " if(true) {} else foo()\n" + + "}", + + "fun foo() {\n" + + " if(true) {} else {foo()\n" + + "}" + ) + + fun testDoNotAutoCloseBraceInUnfinishedTryOnSameLine() = doTypeTest( + '{', + + "fun foo() {\n" + + " try foo()\n" + + "}", + + "fun foo() {\n" + + " try {foo()\n" + + "}" + ) + + fun testDoNotAutoCloseBraceInUnfinishedCatchOnSameLine() = doTypeTest( + '{', + + "fun foo() {\n" + + " try {} catch (e: Exception) foo()\n" + + "}", + + "fun foo() {\n" + + " try {} catch (e: Exception) {foo()\n" + + "}" + ) + + fun testDoNotAutoCloseBraceInUnfinishedFinallyOnSameLine() = doTypeTest( + '{', + + "fun foo() {\n" + + " try {} catch (e: Exception) finally foo()\n" + + "}", + + "fun foo() {\n" + + " try {} catch (e: Exception) finally {foo()\n" + + "}" + ) + + fun testDoNotAutoCloseBraceInUnfinishedWhileSurroundOnSameLine() = doTypeTest( + '{', + + "fun foo() {\n" + + " while() foo()\n" + + "}", + + "fun foo() {\n" + + " while() {foo()\n" + + "}" + ) + + fun testDoNotAutoCloseBraceInUnfinishedWhileSurroundOnNewLine() = doTypeTest( + '{', + + "fun foo() {\n" + + " while()\n" + + "\n" + + " foo()\n" + + "}", + + "fun foo() {\n" + + " while()\n" + + " {\n" + + " foo()\n" + + "}" + ) + + fun testDoNotAutoCloseBraceInUnfinishedIfSurroundOnOtherLine() = doTypeTest( + '{', + + "fun foo() {\n" + + " if(true) \n" + + " foo()\n" + + "}", + + "fun foo() {\n" + + " if(true) {\n" + + " foo()\n" + + "}" + ) + + fun testDoNotAutoCloseBraceInUnfinishedElseSurroundOnOtherLine() = doTypeTest( + '{', + + "fun foo() {\n" + + " if(true) {} else \n" + + " foo()\n" + + "}", + + "fun foo() {\n" + + " if(true) {} else {\n" + + " foo()\n" + + "}" + ) + + fun testDoNotAutoCloseBraceInUnfinishedTryOnOtherLine() = doTypeTest( + '{', + + "fun foo() {\n" + + " try \n" + + " foo()\n" + + "}", + + "fun foo() {\n" + + " try {\n" + + " foo()\n" + + "}" + ) + + fun testDoNotAutoCloseBraceInUnfinishedIfSurroundOnNewLine() = doTypeTest( + '{', + + "fun foo() {\n" + + " if(true)\n" + + " \n" + + " foo()\n" + + "}", + + "fun foo() {\n" + + " if(true)\n" + + " {\n" + + " foo()\n" + + "}" + ) + + fun testDoNotAutoCloseBraceInUnfinishedElseSurroundOnNewLine() = doTypeTest( + '{', + + "fun foo() {\n" + + " if(true) {} else\n" + + " \n" + + " foo()\n" + + "}", + + "fun foo() {\n" + + " if(true) {} else\n" + + " {\n" + + " foo()\n" + + "}" + ) + + fun testDoNotAutoCloseBraceInUnfinishedTryOnNewLine() = doTypeTest( + '{', + + "fun foo() {\n" + + " try\n" + + " \n" + + " foo()\n" + + "}", + + "fun foo() {\n" + + " try\n" + + " {\n" + + " foo()\n" + + "}" + ) + + fun testAutoCloseBraceInsideFor() = doTypeTest( + '{', + + "fun foo() {\n" + + " for (elem in some.filter ) {\n" + + " }\n" + + "}", + + "fun foo() {\n" + + " for (elem in some.filter {}) {\n" + + " }\n" + + "}" + ) + + fun testAutoCloseBraceInsideForAfterCloseParen() = doTypeTest( + '{', + + "fun foo() {\n" + + " for (elem in some.foo(true) ) {\n" + + " }\n" + + "}", + + "fun foo() {\n" + + " for (elem in some.foo(true) {}) {\n" + + " }\n" + + "}" + ) + + fun testAutoCloseBraceBeforeIf() = doTypeTest( + '{', + + "fun foo() {\n" + + " if (true) {}\n" + + "}", + + "fun foo() {\n" + + " {if (true) {}\n" + + "}" + ) + + fun testAutoCloseBraceInIfCondition() = doTypeTest( + '{', + + "fun foo() {\n" + + " if (some.hello (12) )\n" + + "}", + + "fun foo() {\n" + + " if (some.hello (12) {})\n" + + "}" + ) + + fun testInsertSpaceAfterRightBraceOfNestedLambda() = doTypeTest( + '{', + "val t = Array(100) { Array(200) }", + "val t = Array(100) { Array(200) {} }" + ) + + fun testAutoInsertParenInStringLiteral() = doTypeTest( + '(', + """fun f() { println("$dollar{f}") }""", + """fun f() { println("$dollar{f()}") }""" + ) + + fun testAutoInsertParenInCode() = doTypeTest( + '(', + """fun f() { val a = f }""", + """fun f() { val a = f() }""" + ) + + fun testSplitStringByEnter() = doTypeTest( + '\n', + """val s = "foobar"""", + "val s = \"foo\" +\n" + + " \"bar\"" + ) + + fun testSplitStringByEnterEmpty() = doTypeTest( + '\n', + """val s = """"", + "val s = \"\" +\n" + + " \"\"" + ) + + fun testSplitStringByEnterBeforeEscapeSequence() = doTypeTest( + '\n', + """val s = "foo\nbar"""", + "val s = \"foo\" +\n" + + " \"\\nbar\"" + ) + + fun testSplitStringByEnterBeforeSubstitution() = doTypeTest( + '\n', + """val s = "foo${dollar}bar"""", + "val s = \"foo\" +\n" + + " \"${dollar}bar\"" + ) + + fun testSplitStringByEnterAddParentheses() = doTypeTest( + '\n', + """val l = "foobar".length()""", + "val l = (\"foo\" +\n" + + " \"bar\").length()" + ) + + fun testSplitStringByEnterExistingParentheses() = doTypeTest( + '\n', + """val l = ("asdf" + "foobar").length()""", + "val l = (\"asdf\" + \"foo\" +\n" + + " \"bar\").length()" + ) + + fun testTypeLtInFunDeclaration() { + doLtGtTest("fun ") + } + + fun testTypeLtInOngoingConstructorCall() { + doLtGtTest("fun test() { Collection }") + } + + fun testTypeLtInClassDeclaration() { + doLtGtTest("class Some {}") + } + + fun testTypeLtInPropertyType() { + doLtGtTest("val a: List ") + } + + fun testTypeLtInExtensionFunctionReceiver() { + doLtGtTest("fun Collection ") + } + + fun testTypeLtInFunParam() { + doLtGtTest("fun some(a : HashSet)") + } + + fun testTypeLtInFun() { + doLtGtTestNoAutoClose("fun some() { < }") + } + + fun testTypeLtInLess() { + doLtGtTestNoAutoClose("fun some() { val a = 12; a < }") + } + + fun testColonOfSuperTypeList() { + doTypeTest( + ':', + """ + |open class A + |class B + | + """, + """ + |open class A + |class B + | : + """) + } + + fun testColonOfSuperTypeListInObject() { + doTypeTest( + ':', + """ + |interface A + |object B + | + """, + """ + |interface A + |object B + | : + """) + } + + fun testColonOfSuperTypeListInCompanionObject() { + doTypeTest( + ':', + """ + |interface A + |class B { + | companion object + | + |} + """, + """ + |interface A + |class B { + | companion object + | : + |} + """) + } + + fun testColonOfSuperTypeListBeforeBody() { + doTypeTest( + ':', + """ + |open class A + |class B + | { + |} + """, + """ + |open class A + |class B + | : { + |} + """) + } + + fun testColonOfSuperTypeListNotNullIndent() { + doTypeTest( + ':', + """ + |fun test() { + | open class A + | class B + | + |} + """, + """ + |fun test() { + | open class A + | class B + | : + |} + """) + } + + fun testChainCallContinueWithDot() { + doTypeTest( + '.', + """ + |class Test{ fun test() = this } + |fun some() { + | Test() + | + |} + """, + """ + |class Test{ fun test() = this } + |fun some() { + | Test() + | . + |} + """) + } + + fun testChainCallContinueWithSafeCall() { + doTypeTest( + '.', + """ + |class Test{ fun test() = this } + |fun some() { + | Test() + | ? + |} + """, + """ + |class Test{ fun test() = this } + |fun some() { + | Test() + | ?. + |} + """) + } + + fun testContinueWithElvis() { + doTypeTest( + ':', + """ + |fun test(): Any? = null + |fun some() { + | test() + | ? + |} + """, + """ + |fun test(): Any? = null + |fun some() { + | test() + | ?: + |} + """ + ) + } + + fun testContinueWithOr() { + doTypeTest( + '|', + """ + |fun some() { + | if (true + | |) + |} + """, + """ + |fun some() { + | if (true + | ||) + |} + """ + ) + } + + fun testContinueWithAnd() { + doTypeTest( + '&', + """ + |fun some() { + | val test = true + | & + |} + """, + """ + |fun some() { + | val test = true + | && + |} + """ + ) + } + + fun testSpaceAroundRange() { + doTypeTest( + '.', + """ + | val test = 1 + """, + """ + | val test = 1 . + """ + ) + } + + fun testIndentBeforeElseWithBlock() { + doTypeTest( + '\n', + """ + |fun test(b: Boolean) { + | if (b) { + | } + | else if (!b) { + | } + |} + """, + """ + |fun test(b: Boolean) { + | if (b) { + | } + | + | else if (!b) { + | } + |} + """ + ) + } + + fun testIndentBeforeElseWithoutBlock() { + doTypeTest( + '\n', + """ + |fun test(b: Boolean) { + | if (b) + | foo() + | else { + | } + |} + """, + """ + |fun test(b: Boolean) { + | if (b) + | foo() + | + | else { + | } + |} + """ + ) + } + + fun testIndentOnFinishedVariableEndAfterEquals() { + doTypeTest( + '\n', + """ + |fun test() { + | val a = + | foo() + |} + """, + """ + |fun test() { + | val a = + | + | foo() + |} + """ + ) + } + + fun testIndentNotFinishedVariableEndAfterEquals() { + doTypeTest( + '\n', + """ + |fun test() { + | val a = + |} + """, + """ + |fun test() { + | val a = + | + |} + """ + ) + } + + fun testSmartEnterWithTabsOnConstructorParameters() { + doTypeTest( + '\n', + """ + |class A( + | a: Int, + |) + """, + """ + |class A( + | a: Int, + | + |) + """, + enableSmartEnterWithTabs() + ) + } + + fun testSmartEnterWithTabsInMethodParameters() { + doTypeTest( + '\n', + """ + |fun method( + | arg1: String, + |) {} + """, + """ + |fun method( + | arg1: String, + | + |) {} + """, + enableSmartEnterWithTabs() + ) + } + + fun testAutoIndentInWhenClause() { + doTypeTest( + '\n', + """ + |fun test() { + | when (2) { + | is Int -> + | } + |} + """, + """ + |fun test() { + | when (2) { + | is Int -> + | + | } + |} + """ + ) + } + + fun testEnterInFunctionWithExpressionBody() { + doTypeTest( + '\n', + """ + |fun test() = + """, + """ + |fun test() = + | + """, + ENABLE_KOTLIN_OFFICIAL_CODE_STYLE + ) + } + + fun testEnterInMultiDeclaration() { + doTypeTest( + '\n', + """ + |fun test() { + | val (a, b) = + |} + """, + """ + |fun test() { + | val (a, b) = + | + |} + """, + ENABLE_KOTLIN_OFFICIAL_CODE_STYLE + ) + } + + fun testEnterInVariableDeclaration() { + doTypeTest( + '\n', + """ + |val test = + """, + """ + |val test = + | + """, + ENABLE_KOTLIN_OFFICIAL_CODE_STYLE + ) + } + + fun testMoveThroughGT() { + configureFromFileText("a.kt", "val a: List>>") + EditorTestUtil.performTypingAction(editor, '>') + EditorTestUtil.performTypingAction(editor, '>') + checkResultByText("val a: List>") + } + + fun testCharClosingQuote() { + doTypeTest('\'', "val c = ", "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("", "<>") else initText.replace("", "<")) + + 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) + } + } +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/kdoc/KdocRenameTest.java.193 b/idea/tests/org/jetbrains/kotlin/idea/kdoc/KdocRenameTest.java.193 new file mode 100644 index 00000000000..151c8bec854 --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/kdoc/KdocRenameTest.java.193 @@ -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"); + } +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/navigation/AbstractKotlinGotoImplementationTest.kt.193 b/idea/tests/org/jetbrains/kotlin/idea/navigation/AbstractKotlinGotoImplementationTest.kt.193 new file mode 100644 index 00000000000..2740e039513 --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/navigation/AbstractKotlinGotoImplementationTest.kt.193 @@ -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) + } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/InplaceRenameTest.kt.193 b/idea/tests/org/jetbrains/kotlin/idea/refactoring/InplaceRenameTest.kt.193 new file mode 100644 index 00000000000..643ff9a9aaa --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/InplaceRenameTest.kt.193 @@ -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(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(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(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()) + } + } + } +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/rename/SimpleNameReferenceRenameTest.java.193 b/idea/tests/org/jetbrains/kotlin/idea/refactoring/rename/SimpleNameReferenceRenameTest.java.193 new file mode 100644 index 00000000000..9dd37984c8f --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/rename/SimpleNameReferenceRenameTest.java.193 @@ -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"); + } +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/script/AbstractScriptConfigurationTest.kt.193 b/idea/tests/org/jetbrains/kotlin/idea/script/AbstractScriptConfigurationTest.kt.193 new file mode 100644 index 00000000000..6f27881dd0f --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/script/AbstractScriptConfigurationTest.kt.193 @@ -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() + 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)?.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 { + 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 { + 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() + } +} diff --git a/idea/tests/org/jetbrains/kotlin/test/util/ProjectStructureUtils.kt.193 b/idea/tests/org/jetbrains/kotlin/test/util/ProjectStructureUtils.kt.193 new file mode 100644 index 00000000000..c58286d5def --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/test/util/ProjectStructureUtils.kt.193 @@ -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) diff --git a/j2k/tests/org/jetbrains/kotlin/j2k/AbstractJavaToKotlinConverterForWebDemoTest.kt.193 b/j2k/tests/org/jetbrains/kotlin/j2k/AbstractJavaToKotlinConverterForWebDemoTest.kt.193 new file mode 100644 index 00000000000..d3eecdc26f6 --- /dev/null +++ b/j2k/tests/org/jetbrains/kotlin/j2k/AbstractJavaToKotlinConverterForWebDemoTest.kt.193 @@ -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() + override fun setNullables(vararg p0: String) = Unit + override fun getNotNulls() = emptyList() + 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) = Unit + override fun getInstrumentedNotNulls() = emptyList() + }) + + 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 + } +} diff --git a/plugins/uast-kotlin/tests/KotlinUastReferencesTest.kt.193 b/plugins/uast-kotlin/tests/KotlinUastReferencesTest.kt.193 new file mode 100644 index 00000000000..780d2d60320 --- /dev/null +++ b/plugins/uast-kotlin/tests/KotlinUastReferencesTest.kt.193 @@ -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 myField: 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) { + override fun resolve(): PsiMethod? { + val psiClass = JavaPsiFacade.getInstance(element.project).findClass(className, element.resolveScope) ?: return null + val name = element.toUElementOfType()?.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 = 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(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) +// }) + +} + + + +