diff --git a/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt b/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt index 28e12756785..50d37f3bc15 100644 --- a/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt +++ b/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt @@ -41,6 +41,7 @@ import org.jetbrains.kotlin.generators.tests.generator.TestGenerator.TargetBacke import org.jetbrains.kotlin.generators.tests.reservedWords.generateTestDataForReservedWords import org.jetbrains.kotlin.idea.AbstractExpressionSelectionTest import org.jetbrains.kotlin.idea.AbstractSmartSelectionTest +import org.jetbrains.kotlin.idea.actions.AbstractGotoTestOrCodeActionTest import org.jetbrains.kotlin.idea.codeInsight.* import org.jetbrains.kotlin.idea.codeInsight.generate.AbstractGenerateActionTest import org.jetbrains.kotlin.idea.codeInsight.moveUpDown.AbstractCodeMoverTest @@ -411,6 +412,10 @@ fun main(args: Array) { model("navigation/implementations", recursive = false) } + testClass() { + model("navigation/gotoTestOrCode", pattern = "^(.+)\\.main\\..+\$") + } + testClass() { model("quickfix", pattern = """^(\w+)\.before\.Main\.\w+$""", testMethod = "doTestWithExtraFile") } diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 0e8608dd115..4bdf6d2771e 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -612,6 +612,9 @@ + + + org.jetbrains.kotlin.idea.intentions.IfNullToElvisIntention Kotlin diff --git a/idea/src/org/jetbrains/kotlin/idea/testIntegration/KotlinTestCreator.kt b/idea/src/org/jetbrains/kotlin/idea/testIntegration/KotlinTestCreator.kt new file mode 100644 index 00000000000..37fea90e995 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/testIntegration/KotlinTestCreator.kt @@ -0,0 +1,32 @@ +/* + * 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.idea.testIntegration + +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiFile +import com.intellij.testIntegration.TestCreator + +class KotlinTestCreator : TestCreator { + override fun isAvailable(project: Project, editor: Editor, file: PsiFile): Boolean { + return KotlinCreateTestIntention().isAvailable(project, editor, file) + } + + override fun createTest(project: Project, editor: Editor, file: PsiFile) { + KotlinCreateTestIntention().invoke(project, editor, file) + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/testIntegration/KotlinTestFinder.kt b/idea/src/org/jetbrains/kotlin/idea/testIntegration/KotlinTestFinder.kt new file mode 100644 index 00000000000..14b0fc1283f --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/testIntegration/KotlinTestFinder.kt @@ -0,0 +1,102 @@ +/* + * 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.idea.testIntegration + +import com.intellij.codeInsight.TestFrameworks +import com.intellij.openapi.util.Pair +import com.intellij.openapi.util.text.StringUtil +import com.intellij.psi.PsiClass +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiNamedElement +import com.intellij.psi.search.PsiShortNamesCache +import com.intellij.testIntegration.JavaTestFinder +import com.intellij.testIntegration.TestFinderHelper +import com.intellij.util.CommonProcessors +import com.intellij.util.containers.HashSet +import org.jetbrains.kotlin.asJava.KotlinLightClassForExplicitDeclaration +import org.jetbrains.kotlin.asJava.toLightClass +import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny +import org.jetbrains.kotlin.psi.JetClassOrObject +import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf +import java.util.* +import java.util.regex.Pattern + +// Based on com.intellij.testIntegration.JavaTestFinder.JavaTestFinder implementation +// TODO: We can reuse JavaTestFinder if Kotlin classes have their isPhysical() return true +class KotlinTestFinder : JavaTestFinder() { + override fun findSourceElement(from: PsiElement): PsiClass? { + super.findSourceElement(from)?.let { return it } + + val classOrObject = from.parentsWithSelf.filterIsInstance().firstOrNull { !it.isLocal() } ?: return null + if (classOrObject.resolveToDescriptorIfAny() == null) return null + return classOrObject.toLightClass() + } + + override fun isTest(element: PsiElement): Boolean { + val sourceElement = findSourceElement(element) ?: return false + return super.isTest(sourceElement) + } + + override fun findClassesForTest(element: PsiElement): Collection { + val klass = findSourceElement(element) ?: return emptySet() + + val scope = getSearchScope(element, true) + + val cache = PsiShortNamesCache.getInstance(element.project) + + val frameworks = TestFrameworks.getInstance() + val classesWithWeights = ArrayList>() + for (candidateNameWithWeight in TestFinderHelper.collectPossibleClassNamesWithWeights(klass.name)) { + for (eachClass in cache.getClassesByName(candidateNameWithWeight.first, scope)) { + if (eachClass.isAnnotationType || frameworks.isTestClass(eachClass)) continue + if (!eachClass.isPhysical && eachClass !is KotlinLightClassForExplicitDeclaration) continue + + classesWithWeights.add(Pair.create(eachClass, candidateNameWithWeight.second)) + } + } + + return TestFinderHelper.getSortedElements(classesWithWeights, false) + } + + override fun findTestsForClass(element: PsiElement): Collection { + val klass = findSourceElement(element) ?: return emptySet() + + val classesWithProximities = ArrayList>() + val processor = CommonProcessors.CollectProcessor(classesWithProximities) + + val klassName = klass.name!! + val pattern = Pattern.compile(".*" + StringUtil.escapeToRegexp(klassName) + ".*", Pattern.CASE_INSENSITIVE) + + val scope = getSearchScope(klass, false) + val frameworks = TestFrameworks.getInstance() + + val cache = PsiShortNamesCache.getInstance(klass.project) + val names = HashSet() + cache.getAllClassNames(names) + + for (candidateName in names) { + if (!pattern.matcher(candidateName).matches()) continue + for (candidateClass in cache.getClassesByName(candidateName, scope)) { + if (!(frameworks.isTestClass(candidateClass) || frameworks.isPotentialTestClass(candidateClass))) continue + if (!candidateClass.isPhysical && candidateClass !is KotlinLightClassForExplicitDeclaration) continue + processor.process(Pair.create(candidateClass, TestFinderHelper.calcTestNameProximity(klassName, candidateName))) + } + } + + return TestFinderHelper.getSortedElements(classesWithProximities, true) + } +} \ No newline at end of file diff --git a/idea/testData/navigation/gotoTestOrCode/fromJavaClassToTest.kt b/idea/testData/navigation/gotoTestOrCode/fromJavaClassToTest.kt new file mode 100644 index 00000000000..0307ddb16d9 --- /dev/null +++ b/idea/testData/navigation/gotoTestOrCode/fromJavaClassToTest.kt @@ -0,0 +1,3 @@ +import junit.framework.TestCase + +class FooTest2 : TestCase() \ No newline at end of file diff --git a/idea/testData/navigation/gotoTestOrCode/fromJavaClassToTest.main.java b/idea/testData/navigation/gotoTestOrCode/fromJavaClassToTest.main.java new file mode 100644 index 00000000000..188e54ccf24 --- /dev/null +++ b/idea/testData/navigation/gotoTestOrCode/fromJavaClassToTest.main.java @@ -0,0 +1,12 @@ +// CONFIGURE_LIBRARY: JUnit@lib/junit-4.12.jar +// REF: ().FooTest2 +// REF: FooTest +import junit.framework.TestCase; + +public class Foo { + +} + +public class FooTest extends TestCase { + +} \ No newline at end of file diff --git a/idea/testData/navigation/gotoTestOrCode/fromJavaTestToKotlinClass.kt b/idea/testData/navigation/gotoTestOrCode/fromJavaTestToKotlinClass.kt new file mode 100644 index 00000000000..7fa2893941e --- /dev/null +++ b/idea/testData/navigation/gotoTestOrCode/fromJavaTestToKotlinClass.kt @@ -0,0 +1,5 @@ +import junit.framework.TestCase + +class Foo + +class FooTest : TestCase() \ No newline at end of file diff --git a/idea/testData/navigation/gotoTestOrCode/fromJavaTestToKotlinClass.main.java b/idea/testData/navigation/gotoTestOrCode/fromJavaTestToKotlinClass.main.java new file mode 100644 index 00000000000..8ae13c684a9 --- /dev/null +++ b/idea/testData/navigation/gotoTestOrCode/fromJavaTestToKotlinClass.main.java @@ -0,0 +1,7 @@ +// CONFIGURE_LIBRARY: JUnit@lib/junit-4.12.jar +// REF: ().Foo +import junit.framework.TestCase; + +public class FooTest extends TestCase { + +} \ No newline at end of file diff --git a/idea/testData/navigation/gotoTestOrCode/fromKotlinClassToTest.java b/idea/testData/navigation/gotoTestOrCode/fromKotlinClassToTest.java new file mode 100644 index 00000000000..6c98d72a928 --- /dev/null +++ b/idea/testData/navigation/gotoTestOrCode/fromKotlinClassToTest.java @@ -0,0 +1,5 @@ +import junit.framework.TestCase; + +public class FooTest2 extends TestCase { + +} \ No newline at end of file diff --git a/idea/testData/navigation/gotoTestOrCode/fromKotlinClassToTest.main.kt b/idea/testData/navigation/gotoTestOrCode/fromKotlinClassToTest.main.kt new file mode 100644 index 00000000000..ee2cf7fb5aa --- /dev/null +++ b/idea/testData/navigation/gotoTestOrCode/fromKotlinClassToTest.main.kt @@ -0,0 +1,8 @@ +// CONFIGURE_LIBRARY: JUnit@lib/junit-4.12.jar +// REF: ().FooTest +// REF: FooTest2 +import junit.framework.TestCase + +class Foo + +class FooTest : TestCase() \ No newline at end of file diff --git a/idea/testData/navigation/gotoTestOrCode/fromKotlinTestToJavaClass.java b/idea/testData/navigation/gotoTestOrCode/fromKotlinTestToJavaClass.java new file mode 100644 index 00000000000..881e8b173fd --- /dev/null +++ b/idea/testData/navigation/gotoTestOrCode/fromKotlinTestToJavaClass.java @@ -0,0 +1,9 @@ +import junit.framework.TestCase; + +public class Foo { + +} + +public class FooTest2 extends TestCase { + +} \ No newline at end of file diff --git a/idea/testData/navigation/gotoTestOrCode/fromKotlinTestToJavaClass.main.kt b/idea/testData/navigation/gotoTestOrCode/fromKotlinTestToJavaClass.main.kt new file mode 100644 index 00000000000..9338d82ecba --- /dev/null +++ b/idea/testData/navigation/gotoTestOrCode/fromKotlinTestToJavaClass.main.kt @@ -0,0 +1,5 @@ +// CONFIGURE_LIBRARY: JUnit@lib/junit-4.12.jar +// REF: Foo +import junit.framework.TestCase + +class FooTest2 : TestCase() \ No newline at end of file diff --git a/idea/testData/navigation/gotoTestOrCode/fromKotlinTestToKotlinClass.main.kt b/idea/testData/navigation/gotoTestOrCode/fromKotlinTestToKotlinClass.main.kt new file mode 100644 index 00000000000..dd319932e6f --- /dev/null +++ b/idea/testData/navigation/gotoTestOrCode/fromKotlinTestToKotlinClass.main.kt @@ -0,0 +1,9 @@ +// CONFIGURE_LIBRARY: JUnit@lib/junit-4.12.jar +// REF: ().Foo +import junit.framework.TestCase + +class Foo + +class FooTest : TestCase() + +class FooTest2 : TestCase() \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/actions/AbstractGotoTestOrCodeActionTest.kt b/idea/tests/org/jetbrains/kotlin/idea/actions/AbstractGotoTestOrCodeActionTest.kt new file mode 100644 index 00000000000..87390622384 --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/actions/AbstractGotoTestOrCodeActionTest.kt @@ -0,0 +1,71 @@ +/* + * 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.idea.actions + +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.util.io.FileUtil +import com.intellij.psi.PsiFile +import com.intellij.testFramework.PlatformTestUtil +import com.intellij.testIntegration.GotoTestOrCodeHandler +import org.jetbrains.kotlin.idea.navigation.NavigationTestUtils +import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil +import org.jetbrains.kotlin.idea.test.JetLightCodeInsightFixtureTestCase +import org.jetbrains.kotlin.idea.test.JetWithJdkAndRuntimeLightProjectDescriptor +import org.jetbrains.kotlin.idea.test.PluginTestCaseBase +import org.jetbrains.kotlin.test.InTextDirectivesUtils +import org.jetbrains.kotlin.test.JetTestUtils +import java.io.File + +abstract class AbstractGotoTestOrCodeActionTest : JetLightCodeInsightFixtureTestCase() { + private object Handler: GotoTestOrCodeHandler() { + public override fun getSourceAndTargetElements(editor: Editor?, file: PsiFile?) = super.getSourceAndTargetElements(editor, file) + } + + override fun getProjectDescriptor() = JetWithJdkAndRuntimeLightProjectDescriptor.INSTANCE + + protected fun doTest(path: String) { + val mainFile = File(path) + val fileText = FileUtil.loadFile(mainFile, true) + val addKotlinRuntime = InTextDirectivesUtils.findStringWithPrefixes(fileText, "// WITH_RUNTIME") != null + + try { + if (addKotlinRuntime) { + ConfigLibraryUtil.configureKotlinRuntimeAndSdk(myModule, PluginTestCaseBase.mockJdk()) + } + ConfigLibraryUtil.configureLibrariesByDirective(myModule, PlatformTestUtil.getCommunityPath(), fileText) + + myFixture.testDataPath = "${JetTestUtils.getHomeDirectory()}/${mainFile.getParent()}" + + val mainFileName = mainFile.name + val mainFileBaseName = mainFileName.substring(0, mainFileName.indexOf('.')) + mainFile.parentFile + .listFiles { file, name -> + name != mainFileName && name.startsWith("$mainFileBaseName.") && (name.endsWith(".kt") || name.endsWith(".java")) + } + .forEach{ myFixture.configureByFile(it.name) } + val file = myFixture.configureByFile(mainFileName) + + NavigationTestUtils.assertGotoDataMatching(editor, Handler.getSourceAndTargetElements(editor, file)) + } + finally { + ConfigLibraryUtil.unconfigureLibrariesByDirective(myModule, fileText) + if (addKotlinRuntime) { + ConfigLibraryUtil.unConfigureKotlinRuntimeAndSdk(myModule, PluginTestCaseBase.mockJdk()) + } + } + } +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/actions/GotoTestOrCodeActionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/actions/GotoTestOrCodeActionTestGenerated.java new file mode 100644 index 00000000000..463e58e8f0b --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/actions/GotoTestOrCodeActionTestGenerated.java @@ -0,0 +1,67 @@ +/* + * 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.idea.actions; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.JUnit3RunnerWithInners; +import org.jetbrains.kotlin.test.JetTestUtils; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.runner.RunWith; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("idea/testData/navigation/gotoTestOrCode") +@TestDataPath("$PROJECT_ROOT") +@RunWith(JUnit3RunnerWithInners.class) +public class GotoTestOrCodeActionTestGenerated extends AbstractGotoTestOrCodeActionTest { + public void testAllFilesPresentInGotoTestOrCode() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/navigation/gotoTestOrCode"), Pattern.compile("^(.+)\\.main\\..+$"), true); + } + + @TestMetadata("fromJavaClassToTest.main.java") + public void testFromJavaClassToTest() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/navigation/gotoTestOrCode/fromJavaClassToTest.main.java"); + doTest(fileName); + } + + @TestMetadata("fromJavaTestToKotlinClass.main.java") + public void testFromJavaTestToKotlinClass() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/navigation/gotoTestOrCode/fromJavaTestToKotlinClass.main.java"); + doTest(fileName); + } + + @TestMetadata("fromKotlinClassToTest.main.kt") + public void testFromKotlinClassToTest() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/navigation/gotoTestOrCode/fromKotlinClassToTest.main.kt"); + doTest(fileName); + } + + @TestMetadata("fromKotlinTestToJavaClass.main.kt") + public void testFromKotlinTestToJavaClass() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/navigation/gotoTestOrCode/fromKotlinTestToJavaClass.main.kt"); + doTest(fileName); + } + + @TestMetadata("fromKotlinTestToKotlinClass.main.kt") + public void testFromKotlinTestToKotlinClass() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/navigation/gotoTestOrCode/fromKotlinTestToKotlinClass.main.kt"); + doTest(fileName); + } +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/navigation/AbstractKotlinGotoImplementationTest.kt b/idea/tests/org/jetbrains/kotlin/idea/navigation/AbstractKotlinGotoImplementationTest.kt index 80bc1868a10..25aed4f67f0 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/navigation/AbstractKotlinGotoImplementationTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/navigation/AbstractKotlinGotoImplementationTest.kt @@ -32,6 +32,6 @@ public abstract class AbstractKotlinGotoImplementationTest : LightCodeInsightTes protected fun doTest(path: String) { configureByFile(path) val gotoData = NavigationTestUtils.invokeGotoImplementations(LightPlatformCodeInsightTestCase.getEditor(), LightPlatformCodeInsightTestCase.getFile()) - NavigationTestUtils.assertGotoImplementations(LightPlatformCodeInsightTestCase.getEditor(), gotoData) + NavigationTestUtils.assertGotoDataMatching(LightPlatformCodeInsightTestCase.getEditor(), gotoData) } } \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/navigation/JetGotoImplementationMultifileTest.java b/idea/tests/org/jetbrains/kotlin/idea/navigation/JetGotoImplementationMultifileTest.java index d4121c08eaa..4947a93ad7b 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/navigation/JetGotoImplementationMultifileTest.java +++ b/idea/tests/org/jetbrains/kotlin/idea/navigation/JetGotoImplementationMultifileTest.java @@ -83,7 +83,7 @@ public class JetGotoImplementationMultifileTest extends JetLightCodeInsightFixtu private void doMultifileTest(String ... fileNames) throws Exception { myFixture.configureByFiles(fileNames); GotoTargetHandler.GotoData gotoData = NavigationTestUtils.invokeGotoImplementations(getEditor(), getFile()); - NavigationTestUtils.assertGotoImplementations(getEditor(), gotoData); + NavigationTestUtils.assertGotoDataMatching(getEditor(), gotoData); } @Override diff --git a/idea/tests/org/jetbrains/kotlin/idea/navigation/NavigationTestUtils.java b/idea/tests/org/jetbrains/kotlin/idea/navigation/NavigationTestUtils.java index 8d83db22dba..e58f1413e9b 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/navigation/NavigationTestUtils.java +++ b/idea/tests/org/jetbrains/kotlin/idea/navigation/NavigationTestUtils.java @@ -48,7 +48,7 @@ public final class NavigationTestUtils { return new GotoImplementationHandler().getSourceAndTargetElements(editor, psiFile); } - public static void assertGotoImplementations(Editor editor, GotoTargetHandler.GotoData gotoData) { + public static void assertGotoDataMatching(Editor editor, GotoTargetHandler.GotoData gotoData) { // Get expected references from the tested document List expectedReferences = InTextDirectivesUtils.findListWithPrefixes(editor.getDocument().getText(), "// REF:"); Collections.sort(expectedReferences);