Unit Test Tooling: Test navigation
#KT-6472 In Progress
This commit is contained in:
@@ -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<String>) {
|
||||
model("navigation/implementations", recursive = false)
|
||||
}
|
||||
|
||||
testClass<AbstractGotoTestOrCodeActionTest>() {
|
||||
model("navigation/gotoTestOrCode", pattern = "^(.+)\\.main\\..+\$")
|
||||
}
|
||||
|
||||
testClass<AbstractQuickFixMultiFileTest>() {
|
||||
model("quickfix", pattern = """^(\w+)\.before\.Main\.\w+$""", testMethod = "doTestWithExtraFile")
|
||||
}
|
||||
|
||||
@@ -612,6 +612,9 @@
|
||||
|
||||
<codeInsight.typeInfo language="kotlin" implementationClass="org.jetbrains.kotlin.idea.codeInsight.KotlinExpressionTypeProvider"/>
|
||||
|
||||
<testCreator language="kotlin" implementationClass="org.jetbrains.kotlin.idea.testIntegration.KotlinTestCreator"/>
|
||||
<testFinder implementation="org.jetbrains.kotlin.idea.testIntegration.KotlinTestFinder"/>
|
||||
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.kotlin.idea.intentions.IfNullToElvisIntention</className>
|
||||
<category>Kotlin</category>
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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<JetClassOrObject>().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<PsiElement> {
|
||||
val klass = findSourceElement(element) ?: return emptySet()
|
||||
|
||||
val scope = getSearchScope(element, true)
|
||||
|
||||
val cache = PsiShortNamesCache.getInstance(element.project)
|
||||
|
||||
val frameworks = TestFrameworks.getInstance()
|
||||
val classesWithWeights = ArrayList<Pair<out PsiNamedElement, Int>>()
|
||||
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<PsiElement> {
|
||||
val klass = findSourceElement(element) ?: return emptySet()
|
||||
|
||||
val classesWithProximities = ArrayList<Pair<out PsiNamedElement, Int>>()
|
||||
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<String>()
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
import junit.framework.TestCase
|
||||
|
||||
class FooTest2 : TestCase()
|
||||
@@ -0,0 +1,12 @@
|
||||
// CONFIGURE_LIBRARY: JUnit@lib/junit-4.12.jar
|
||||
// REF: (<root>).FooTest2
|
||||
// REF: FooTest
|
||||
import junit.framework.TestCase;
|
||||
|
||||
public class <caret>Foo {
|
||||
|
||||
}
|
||||
|
||||
public class FooTest extends TestCase {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import junit.framework.TestCase
|
||||
|
||||
class Foo
|
||||
|
||||
class FooTest : TestCase()
|
||||
@@ -0,0 +1,7 @@
|
||||
// CONFIGURE_LIBRARY: JUnit@lib/junit-4.12.jar
|
||||
// REF: (<root>).Foo
|
||||
import junit.framework.TestCase;
|
||||
|
||||
public class <caret>FooTest extends TestCase {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import junit.framework.TestCase;
|
||||
|
||||
public class FooTest2 extends TestCase {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// CONFIGURE_LIBRARY: JUnit@lib/junit-4.12.jar
|
||||
// REF: (<root>).FooTest
|
||||
// REF: FooTest2
|
||||
import junit.framework.TestCase
|
||||
|
||||
class <caret>Foo
|
||||
|
||||
class FooTest : TestCase()
|
||||
@@ -0,0 +1,9 @@
|
||||
import junit.framework.TestCase;
|
||||
|
||||
public class Foo {
|
||||
|
||||
}
|
||||
|
||||
public class FooTest2 extends TestCase {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
// CONFIGURE_LIBRARY: JUnit@lib/junit-4.12.jar
|
||||
// REF: Foo
|
||||
import junit.framework.TestCase
|
||||
|
||||
class <caret>FooTest2 : TestCase()
|
||||
@@ -0,0 +1,9 @@
|
||||
// CONFIGURE_LIBRARY: JUnit@lib/junit-4.12.jar
|
||||
// REF: (<root>).Foo
|
||||
import junit.framework.TestCase
|
||||
|
||||
class Foo
|
||||
|
||||
class <caret>FooTest : TestCase()
|
||||
|
||||
class FooTest2 : TestCase()
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -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)
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -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
|
||||
|
||||
@@ -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<String> expectedReferences = InTextDirectivesUtils.findListWithPrefixes(editor.getDocument().getText(), "// REF:");
|
||||
Collections.sort(expectedReferences);
|
||||
|
||||
Reference in New Issue
Block a user