Add test for multi-platform find usages #KT-15665 Obsolete

+ perform minor AbstractFindUsagesTest refactoring
This commit is contained in:
Mikhail Glukhikh
2017-04-20 17:52:57 +03:00
parent 4e8fe18ca8
commit 247a571f51
7 changed files with 255 additions and 137 deletions
@@ -0,0 +1,4 @@
// PSI_ELEMENT: org.jetbrains.kotlin.psi.KtNamedFunction
// OPTIONS: usages
header fun <caret>boo(s: String)
@@ -0,0 +1,2 @@
[jvm.kt] Function call 4 boo("a")
[jvm.kt] Function call 5 boo("b")
@@ -0,0 +1,6 @@
impl fun boo(s: String) {}
fun test() {
boo("a")
boo("b")
}
@@ -0,0 +1,60 @@
/*
* Copyright 2010-2017 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.findUsages
import com.intellij.codeInsight.TargetElementUtil
import com.intellij.testFramework.UsefulTestCase
import org.jetbrains.kotlin.idea.project.PluginJetFilesProvider
import org.jetbrains.kotlin.idea.stubs.AbstractMultiModuleTest
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.test.InTextDirectivesUtils
abstract class AbstractFindUsagesMultiModuleTest : AbstractMultiModuleTest() {
override val testPath = PluginTestCaseBase.getTestDataPathBase() + "/multiModuleFindUsages/"
protected fun doFindUsagesTest() {
val allFilesInProject = PluginJetFilesProvider.allFilesInProject(myProject!!)
val mainFile = allFilesInProject.single { file ->
file.text.contains("// ")
}
val virtualFile = mainFile.virtualFile!!
configureByExistingFile(virtualFile)
val mainFileName = mainFile.name
val mainFileText = mainFile.text
val prefix = mainFileName.substringBefore(".") + "."
val caretElementClassNames = InTextDirectivesUtils.findLinesWithPrefixesRemoved(mainFileText, "// PSI_ELEMENT: ")
@Suppress("UNCHECKED_CAST")
val caretElementClass = Class.forName(caretElementClassNames.single()) as Class<out KtDeclaration>
val parser = OptionsParser.getParserByPsiElementClass(caretElementClass)
val rootPath = virtualFile.path.substringBeforeLast("/") + "/"
val caretElement = TargetElementUtil.findTargetElement(myEditor, TargetElementUtil.ELEMENT_NAME_ACCEPTED)
UsefulTestCase.assertInstanceOf(caretElement!!, caretElementClass)
val options = parser?.parse(mainFileText, project)
findUsagesAndCheckResults(mainFileText, prefix, rootPath, caretElement, options, project, alwaysAppendFileName = true)
}
}
@@ -17,7 +17,7 @@
package org.jetbrains.kotlin.findUsages
import com.intellij.codeInsight.JavaTargetElementEvaluator
import com.intellij.codeInsight.TargetElementUtilBase
import com.intellij.codeInsight.TargetElementUtil
import com.intellij.find.FindManager
import com.intellij.find.findUsages.FindUsagesHandler
import com.intellij.find.findUsages.FindUsagesOptions
@@ -28,6 +28,7 @@ import com.intellij.lang.properties.psi.PropertiesFile
import com.intellij.lang.properties.psi.Property
import com.intellij.openapi.editor.markup.TextAttributes
import com.intellij.openapi.extensions.Extensions
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.util.io.FileUtilRt
import com.intellij.psi.PsiComment
@@ -49,7 +50,6 @@ import org.jetbrains.kotlin.idea.findUsages.KotlinFindUsagesHandlerFactory
import org.jetbrains.kotlin.idea.search.usagesSearch.ExpressionsOfTypeProcessor
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
import org.jetbrains.kotlin.idea.test.TestFixtureExtension
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
import org.jetbrains.kotlin.psi.KtDeclaration
@@ -114,8 +114,8 @@ abstract class AbstractFindUsagesTest : KotlinLightCodeInsightFixtureTestCase()
myFixture.configureByFile(mainFileName)
val caretElement = if (InTextDirectivesUtils.isDirectiveDefined(mainFileText, "// FIND_BY_REF"))
TargetElementUtilBase.findTargetElement(myFixture.editor,
TargetElementUtilBase.REFERENCED_ELEMENT_ACCEPTED or JavaTargetElementEvaluator.NEW_AS_CONSTRUCTOR)!!
TargetElementUtil.findTargetElement(myFixture.editor,
TargetElementUtil.REFERENCED_ELEMENT_ACCEPTED or JavaTargetElementEvaluator.NEW_AS_CONSTRUCTOR)!!
else
myFixture.elementAtCaret
UsefulTestCase.assertInstanceOf(caretElement, caretElementClass)
@@ -128,15 +128,15 @@ abstract class AbstractFindUsagesTest : KotlinLightCodeInsightFixtureTestCase()
// Ensure that search by sources (if present) and decompiled declarations gives the same results
if (isLibraryElement) {
val originalElement = caretElement.originalElement
findUsagesAndCheckResults(mainFileText, prefix, rootPath, originalElement, options)
findUsagesAndCheckResults(mainFileText, prefix, rootPath, originalElement, options, project)
val navigationElement = caretElement.navigationElement
if (navigationElement !== originalElement) {
findUsagesAndCheckResults(mainFileText, prefix, rootPath, navigationElement, options)
findUsagesAndCheckResults(mainFileText, prefix, rootPath, navigationElement, options, project)
}
}
else {
findUsagesAndCheckResults<PsiElement>(mainFileText, prefix, rootPath, caretElement, options)
findUsagesAndCheckResults(mainFileText, prefix, rootPath, caretElement, options, project)
}
}
finally {
@@ -144,138 +144,13 @@ abstract class AbstractFindUsagesTest : KotlinLightCodeInsightFixtureTestCase()
}
}
private fun <T : PsiElement> findUsagesAndCheckResults(
mainFileText: String,
prefix: String,
rootPath: String,
caretElement: T,
options: FindUsagesOptions?
) {
val highlightingMode = InTextDirectivesUtils.isDirectiveDefined(mainFileText, "// HIGHLIGHTING")
var log: String? = null
val logList = ArrayList<String>()
val usageInfos = try {
if (ExpressionsOfTypeProcessor.mode !== ExpressionsOfTypeProcessor.Mode.ALWAYS_PLAIN) {
ExpressionsOfTypeProcessor.testLog = logList
}
if (InTextDirectivesUtils.isDirectiveDefined(mainFileText, "// PLAIN_WHEN_NEEDED")) {
ExpressionsOfTypeProcessor.mode = ExpressionsOfTypeProcessor.Mode.PLAIN_WHEN_NEEDED
}
findUsages(caretElement, options, highlightingMode)
}
finally {
ExpressionsOfTypeProcessor.testLog = null
if (logList.size > 0) {
log = logList.sorted().joinToString("\n")
}
ExpressionsOfTypeProcessor.mode = ExpressionsOfTypeProcessor.Mode.ALWAYS_SMART
}
val filteringRules = instantiateClasses<UsageFilteringRule>(mainFileText, "// FILTERING_RULES: ")
val groupingRules = instantiateClasses<UsageGroupingRule>(mainFileText, "// GROUPING_RULES: ")
val filteredUsages = getUsageAdapters(filteringRules, usageInfos)
val usageFiles = filteredUsages.map { it.file.name }.distinct()
val appendFileName = usageFiles.size > 1
val convertToString: (UsageInfo2UsageAdapter) -> String = { usageAdapter ->
var groupAsString = groupingRules
.map { it.groupUsage(usageAdapter)?.getText(null) ?: "" }
.joinToString(", ")
if (!groupAsString.isEmpty()) {
groupAsString = "($groupAsString) "
}
val usageType = getUsageType(usageAdapter.element)
val usageTypeAsString = usageType?.toString(USAGE_VIEW_PRESENTATION) ?: "null"
val usageChunks = ArrayList<TextChunk>()
usageChunks.addAll(usageAdapter.presentation.text.asList())
usageChunks.add(1, TextChunk(TextAttributes(), " ")) // add space after line number
buildString {
if (appendFileName) {
append("[").append(usageAdapter.file.name).append("] ")
}
append(usageTypeAsString)
append(" ")
append(groupAsString)
append(usageChunks.joinToString(""))
}
}
val finalUsages = filteredUsages.map(convertToString).sorted()
KotlinTestUtils.assertEqualsToFile(File(rootPath, prefix + "results.txt"), finalUsages.joinToString("\n"))
if (log != null) {
KotlinTestUtils.assertEqualsToFile(File(rootPath, prefix + "log"), log)
// if log is empty then compare results with plain search
try {
ExpressionsOfTypeProcessor.mode = ExpressionsOfTypeProcessor.Mode.ALWAYS_PLAIN
findUsagesAndCheckResults(mainFileText, prefix, rootPath, caretElement, options)
}
finally {
ExpressionsOfTypeProcessor.mode = ExpressionsOfTypeProcessor.Mode.ALWAYS_SMART
}
}
}
protected fun findUsages(
targetElement: PsiElement,
options: FindUsagesOptions?,
highlightingMode: Boolean
): Collection<UsageInfo> {
val project = project
val handler: FindUsagesHandler = (if (targetElement is PsiMember) {
JavaFindUsagesHandler(targetElement, JavaFindUsagesHandlerFactory(project))
}
else if (targetElement is KtDeclaration && targetElement !is KtTypeAlias) {
KotlinFindUsagesHandlerFactory(project).createFindUsagesHandlerNoQuestions(targetElement)
}
else {
(FindManager.getInstance(project) as FindManagerImpl).findUsagesManager.getFindUsagesHandler(targetElement, false)
}) ?: error("Cannot find handler for: $targetElement")
@Suppress("NAME_SHADOWING")
val options = options ?: handler.getFindUsagesOptions(null)
options.searchScope = GlobalSearchScope.allScope(project)
val processor = CommonProcessors.CollectProcessor<UsageInfo>()
for (psiElement in handler.primaryElements + handler.secondaryElements) {
if (highlightingMode) {
//TODO: should findReferencesToHighlight work outside read-action or it makes no sense?
for (reference in handler.findReferencesToHighlight(psiElement, options.searchScope)) {
processor.process(UsageInfo(reference))
}
}
else {
// run in another thread to test read-action assertions
val thread = Thread {
handler.processElementUsages(psiElement, processor, options)
}
thread.start()
thread.join()
}
}
return processor.results
}
companion object {
val SUPPORTED_EXTENSIONS = setOf("kt", "java", "xml", "properties", "txt", "groovy")
val USAGE_VIEW_PRESENTATION = UsageViewPresentation()
private fun getUsageAdapters(
internal fun getUsageAdapters(
filters: Collection<UsageFilteringRule>,
usageInfos: Collection<UsageInfo>
): Collection<UsageInfo2UsageAdapter> {
@@ -284,7 +159,7 @@ abstract class AbstractFindUsagesTest : KotlinLightCodeInsightFixtureTestCase()
.filter { usageAdapter -> filters.all { it.isVisible(usageAdapter) } }
}
private fun getUsageType(element: PsiElement?): UsageType? {
internal fun getUsageType(element: PsiElement?): UsageType? {
if (element == null) return null
if (element.getNonStrictParentOfType<PsiComment>() != null) {
@@ -298,7 +173,7 @@ abstract class AbstractFindUsagesTest : KotlinLightCodeInsightFixtureTestCase()
?: UsageType.UNCLASSIFIED
}
private fun <T> instantiateClasses(mainFileText: String, directive: String): Collection<T> {
internal fun <T> instantiateClasses(mainFileText: String, directive: String): Collection<T> {
val filteringRuleClassNames = InTextDirectivesUtils.findLinesWithPrefixesRemoved(mainFileText, directive)
return filteringRuleClassNames
.map {
@@ -308,3 +183,132 @@ abstract class AbstractFindUsagesTest : KotlinLightCodeInsightFixtureTestCase()
}
}
}
internal fun <T : PsiElement> findUsagesAndCheckResults(
mainFileText: String,
prefix: String,
rootPath: String,
caretElement: T,
options: FindUsagesOptions?,
project: Project,
alwaysAppendFileName: Boolean = false
) {
val highlightingMode = InTextDirectivesUtils.isDirectiveDefined(mainFileText, "// HIGHLIGHTING")
var log: String? = null
val logList = ArrayList<String>()
val usageInfos = try {
if (ExpressionsOfTypeProcessor.mode !== ExpressionsOfTypeProcessor.Mode.ALWAYS_PLAIN) {
ExpressionsOfTypeProcessor.testLog = logList
}
if (InTextDirectivesUtils.isDirectiveDefined(mainFileText, "// PLAIN_WHEN_NEEDED")) {
ExpressionsOfTypeProcessor.mode = ExpressionsOfTypeProcessor.Mode.PLAIN_WHEN_NEEDED
}
findUsages(caretElement, options, highlightingMode, project)
}
finally {
ExpressionsOfTypeProcessor.testLog = null
if (logList.size > 0) {
log = logList.sorted().joinToString("\n")
}
ExpressionsOfTypeProcessor.mode = ExpressionsOfTypeProcessor.Mode.ALWAYS_SMART
}
val filteringRules = AbstractFindUsagesTest.instantiateClasses<UsageFilteringRule>(mainFileText, "// FILTERING_RULES: ")
val groupingRules = AbstractFindUsagesTest.instantiateClasses<UsageGroupingRule>(mainFileText, "// GROUPING_RULES: ")
val filteredUsages = AbstractFindUsagesTest.getUsageAdapters(filteringRules, usageInfos)
val usageFiles = filteredUsages.map { it.file.name }.distinct()
val appendFileName = alwaysAppendFileName || usageFiles.size > 1
val convertToString: (UsageInfo2UsageAdapter) -> String = { usageAdapter ->
var groupAsString = groupingRules
.map { it.groupUsage(usageAdapter)?.getText(null) ?: "" }
.joinToString(", ")
if (!groupAsString.isEmpty()) {
groupAsString = "($groupAsString) "
}
val usageType = AbstractFindUsagesTest.getUsageType(usageAdapter.element)
val usageTypeAsString = usageType?.toString(AbstractFindUsagesTest.USAGE_VIEW_PRESENTATION) ?: "null"
val usageChunks = ArrayList<TextChunk>()
usageChunks.addAll(usageAdapter.presentation.text.asList())
usageChunks.add(1, TextChunk(TextAttributes(), " ")) // add space after line number
buildString {
if (appendFileName) {
append("[").append(usageAdapter.file.name).append("] ")
}
append(usageTypeAsString)
append(" ")
append(groupAsString)
append(usageChunks.joinToString(""))
}
}
val finalUsages = filteredUsages.map(convertToString).sorted()
KotlinTestUtils.assertEqualsToFile(File(rootPath, prefix + "results.txt"), finalUsages.joinToString("\n"))
if (log != null) {
KotlinTestUtils.assertEqualsToFile(File(rootPath, prefix + "log"), log)
// if log is empty then compare results with plain search
try {
ExpressionsOfTypeProcessor.mode = ExpressionsOfTypeProcessor.Mode.ALWAYS_PLAIN
findUsagesAndCheckResults(mainFileText, prefix, rootPath, caretElement, options, project)
}
finally {
ExpressionsOfTypeProcessor.mode = ExpressionsOfTypeProcessor.Mode.ALWAYS_SMART
}
}
}
internal fun findUsages(
targetElement: PsiElement,
options: FindUsagesOptions?,
highlightingMode: Boolean,
project: Project
): Collection<UsageInfo> {
val handler: FindUsagesHandler = (if (targetElement is PsiMember) {
JavaFindUsagesHandler(targetElement, JavaFindUsagesHandlerFactory(project))
}
else if (targetElement is KtDeclaration && targetElement !is KtTypeAlias) {
KotlinFindUsagesHandlerFactory(project).createFindUsagesHandlerNoQuestions(targetElement)
}
else {
(FindManager.getInstance(project) as FindManagerImpl).findUsagesManager.getFindUsagesHandler(targetElement, false)
}) ?: error("Cannot find handler for: $targetElement")
@Suppress("NAME_SHADOWING")
val options = options ?: handler.getFindUsagesOptions(null)
options.searchScope = GlobalSearchScope.allScope(project)
val processor = CommonProcessors.CollectProcessor<UsageInfo>()
for (psiElement in handler.primaryElements + handler.secondaryElements) {
if (highlightingMode) {
//TODO: should findReferencesToHighlight work outside read-action or it makes no sense?
for (reference in handler.findReferencesToHighlight(psiElement, options.searchScope)) {
processor.process(UsageInfo(reference))
}
}
else {
// run in another thread to test read-action assertions
val thread = Thread {
handler.processElementUsages(psiElement, processor, options)
}
thread.start()
thread.join()
}
}
return processor.results
}
@@ -0,0 +1,43 @@
/*
* Copyright 2010-2017 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.findUsages
import org.jetbrains.kotlin.config.JvmTarget
import org.jetbrains.kotlin.config.TargetPlatformKind
import org.junit.Test
class FindUsagesMultiModuleTest : AbstractFindUsagesMultiModuleTest() {
private fun doMultiPlatformTest(commonName: String = "common",
implName: String = "jvm",
implKind: TargetPlatformKind<*> = TargetPlatformKind.Jvm[JvmTarget.JVM_1_6]) {
val header = module(commonName)
header.createFacet(TargetPlatformKind.Common)
val jvm = module(implName)
jvm.createFacet(implKind)
jvm.enableMultiPlatform()
jvm.addDependency(header)
doFindUsagesTest()
}
@Test
fun testFindImplFromHeader() {
doMultiPlatformTest()
}
}
@@ -19,14 +19,13 @@ package org.jetbrains.kotlin.findUsages
import com.intellij.psi.search.FilenameIndex
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import kotlin.test.assertEquals
class KotlinFindUsagesWithLibraryCustomTest : AbstractKotlinFindUsagesWithLibraryTest() {
fun testFindUsagesForLocalClassProperty() {
val libraryFile = FilenameIndex.getFilesByName(getProject(), "library.kt", myFixture.module.moduleWithLibrariesScope).first()
val indexOf = libraryFile.text.indexOf("localClassProperty")
val jetParameter = libraryFile.findElementAt(indexOf)!!.getStrictParentOfType<KtParameter>()!!
val usages = findUsages(jetParameter.getOriginalElement(), null, false)
val usages = findUsages(jetParameter.getOriginalElement(), null, false, project)
assertEquals(2, usages.size)
}
}