Better testing via logging of performed operations

This commit is contained in:
Valentin Kipyatkov
2016-08-28 01:25:37 +03:00
parent 1e80ad3f98
commit 2ca163a375
11 changed files with 185 additions and 14 deletions

View File

@@ -38,6 +38,7 @@ import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName
import org.jetbrains.kotlin.idea.references.KtDestructuringDeclarationReference
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinRequestResultProcessor
import org.jetbrains.kotlin.idea.search.restrictToKotlinSources
@@ -61,7 +62,9 @@ enum class DestructuringDeclarationUsageSearch {
ALWAYS_SMART, ALWAYS_PLAIN, PLAIN_WHEN_NEEDED
}
// for tests
var destructuringDeclarationUsageSearchMode = if (ApplicationManager.getApplication().isUnitTestMode) ALWAYS_SMART else PLAIN_WHEN_NEEDED
var destructuringDeclarationUsageSearchLog: MutableList<String>? = null
//TODO: check if it's too expensive
@@ -108,7 +111,8 @@ fun findDestructuringDeclarationUsages(
ktDeclaration,
scope,
consumer,
plainSearchHandler = { searchScope -> doPlainSearch(ktDeclaration, searchScope, optimizer) }
plainSearchHandler = { searchScope -> doPlainSearch(ktDeclaration, searchScope, optimizer) },
testLog = destructuringDeclarationUsageSearchLog
).run()
}
@@ -124,13 +128,15 @@ private class Processor(
private val target: KtDeclaration,
private val searchScope: SearchScope,
private val consumer: Processor<PsiReference>,
private val plainSearchHandler: (SearchScope) -> Unit
plainSearchHandler: (SearchScope) -> Unit,
private val testLog: MutableList<String>?
) {
private val project = target.project
private val plainSearchHandler: (SearchScope) -> Unit = { scope ->
testLog?.add("Used plain search in ${scope.logPresentation()}")
plainSearchHandler(scope)
}
// we don't need to search usages of declarations in Java because Java doesn't have implicitly typed declarations so such usages cannot affect Kotlin code
//TODO: what about Scala and other JVM-languages?
private val declarationUsageScope = GlobalSearchScope.projectScope(project).restrictToKotlinSources()
private val project = target.project
// note: a Task must define equals & hashCode!
private interface Task {
@@ -159,8 +165,10 @@ private class Processor(
val parameters = ClassInheritorsSearch.SearchParameters(psiClass, GlobalSearchScope.allScope(project), true, true, false)
val classesToSearch = listOf(psiClass) + ClassInheritorsSearch.search(parameters).findAll()
testLog?.add("Searched inheritors of ${psiClass.logPresentation()}")
for (classToSearch in classesToSearch) {
testLog?.add("Searched references to ${classToSearch.logPresentation()}")
ReferencesSearch.search(classToSearch).forEach(Processor processor@ { reference -> //TODO: see KT-13607
if (processDataClassUsage(reference)) return@processor true
@@ -211,7 +219,11 @@ private class Processor(
private fun addCallableDeclarationToProcess(declaration: PsiElement, kind: CallableToProcessKind) {
data class ProcessCallableUsagesTask(val declaration: PsiElement, val kind: CallableToProcessKind) : Task {
override fun perform() {
ReferencesSearch.search(declaration, declarationUsageScope).forEach { reference ->
// we don't need to search usages of declarations in Java because Java doesn't have implicitly typed declarations so such usages cannot affect Kotlin code
//TODO: what about Scala and other JVM-languages?
val scope = GlobalSearchScope.projectScope(project).restrictToKotlinSources()
testLog?.add("Searched references to ${declaration.logPresentation()} in Kotlin files")
ReferencesSearch.search(declaration, scope).forEach { reference ->
when (kind) {
CallableToProcessKind.HAS_DATA_CLASS_TYPE -> {
if (reference is KtDestructuringDeclarationReference) {
@@ -247,6 +259,7 @@ private class Processor(
override fun perform() {
//TODO: what about other JVM languages?
val scope = GlobalSearchScope.getScopeRestrictedByFileTypes(GlobalSearchScope.projectScope(project), JavaFileType.INSTANCE)
testLog?.add("Searched references to ${psiClass.logPresentation()} in java files")
ReferencesSearch.search(psiClass, scope).forEach { reference ->
// check if the reference is method parameter type
val parameter = ((reference as? PsiJavaCodeReferenceElement)?.parent as? PsiTypeElement)?.parent as? PsiParameter
@@ -563,4 +576,43 @@ private class Processor(
else -> false
}
}
private fun PsiElement.logPresentation(): String? {
val fqName = getKotlinFqName()?.asString()
?: (this as? KtNamedDeclaration)?.name
return when (this) {
is PsiMethod, is KtFunction -> fqName + "()"
is KtParameter -> "parameter ${this.name} in ${this.ownerFunction?.logPresentation()}"
else -> fqName
}
}
private fun SearchScope.logPresentation(): String {
return when (this) {
searchScope -> "whole search scope"
is LocalSearchScope -> {
scope
.map { element ->
" " + when (element) {
is KtFunctionLiteral -> element.text
is KtWhenEntry -> {
if (element.isElse)
"KtWhenEntry \"else\""
else
"KtWhenEntry \"" + element.conditions.joinToString(", ") { it.text } + "\""
}
is KtNamedDeclaration -> element.node.elementType.toString() + ":" + element.name
else -> element.toString()
}
}
.toList()
.sorted()
.joinToString("\n", "LocalSearchScope:\n")
}
else -> this.displayName
}
}
}

View File

@@ -131,9 +131,10 @@ abstract class KotlinFindMemberUsagesHandler<T : KtNamedDeclaration>
val uniqueProcessor = CommonProcessors.UniqueProcessor(processor)
if (options.isUsages) {
val searchParameters = KotlinReferencesSearchParameters(element,
scope = options.searchScope,
kotlinOptions = createKotlinReferencesSearchOptions(options))
// we disable searchForComponentConventions for ReferencesSearch because they will be searched by MethodReferencesSearch
val kotlinSearchOptions = createKotlinReferencesSearchOptions(options)
.copy(searchForComponentConventions = false)
val searchParameters = KotlinReferencesSearchParameters(element, options.searchScope, kotlinOptions = kotlinSearchOptions)
with(applyQueryFilters(element, options, ReferencesSearch.search(searchParameters))) {
if (!forEach(referenceProcessor)) return false

View File

@@ -0,0 +1,7 @@
Searched inheritors of X
Searched references to X
Searched references to f() in Kotlin files
Used plain search in LocalSearchScope:
CLASS:X
FUN:component1
FUN:component2

View File

@@ -0,0 +1,9 @@
Searched inheritors of X
Searched references to X
Searched references to f() in Kotlin files
Used plain search in LocalSearchScope:
CLASS:X
FUN:component1
FUN:component1
FUN:component2
FUN:component2

View File

@@ -0,0 +1,53 @@
Searched inheritors of pack.A
Searched inheritors of pack.B
Searched inheritors of pack.C
Searched references to JavaClass.getA() in Kotlin files
Searched references to JavaClass.takeSAM() in Kotlin files
Searched references to JavaSAM in java files
Searched references to a in Kotlin files
Searched references to a in Kotlin files
Searched references to a1 in Kotlin files
Searched references to f() in Kotlin files
Searched references to g() in Kotlin files
Searched references to h() in Kotlin files
Searched references to list in Kotlin files
Searched references to listOfA() in Kotlin files
Searched references to pack.A
Searched references to pack.B
Searched references to pack.C
Searched references to pack.C.component1() in Kotlin files
Searched references to pack4.takeExtFun() in Kotlin files
Searched references to pack4.takeFun1() in Kotlin files
Searched references to pack4.takeFun2() in Kotlin files
Searched references to pack4.takeFun3() in Kotlin files
Searched references to pack4.takeFuns() in Kotlin files
Searched references to pack5.v in Kotlin files
Searched references to parameter a in B() in Kotlin files
Searched references to parameter a in null in Kotlin files
Searched references to parameter b in pack.f() in Kotlin files
Searched references to parameter c in pack.f() in Kotlin files
Searched references to parameter p in pack4.foo() in Kotlin files
Searched references to parameter p in pack4.takeExtFun() in Kotlin files
Searched references to parameter p in pack4.takeFun1() in Kotlin files
Searched references to parameter p in pack4.takeFun2() in Kotlin files
Searched references to parameter p in pack4.takeFun3() in Kotlin files
Searched references to parameter p in pack4.takeFuns() in Kotlin files
Used plain search in LocalSearchScope:
CLASS:A
FUN:ext1
FUN:ext1
FUN:x
KtWhenEntry "else"
KtWhenEntry "is A"
{ a, n -> val (x, y) = a!! }
{ val (x, y ) = it }
{ val (x, y) = it }
{ val (x, y) = it }
{ val (x, y) = it }
{ val (x, y) = it }
{ val (x, y) = it[0] }
{ val (x, y) = this }
Used plain search in LocalSearchScope:
CLASS:B
Used plain search in LocalSearchScope:
CLASS:C

View File

@@ -0,0 +1,5 @@
Searched inheritors of A
Searched references to A
Searched references to a in Kotlin files
Used plain search in LocalSearchScope:
CLASS:A

View File

@@ -0,0 +1 @@
Used plain search in whole search scope

View File

@@ -0,0 +1,10 @@
Searched inheritors of X
Searched references to X
Searched references to Y
Searched references to f() in Kotlin files
Used plain search in LocalSearchScope:
CLASS:X
CLASS:Y
FUN:component1
FUN:component2
FUN:ext

View File

@@ -0,0 +1,11 @@
Searched inheritors of X
Searched references to X
Searched references to Y
Searched references to Z
Searched references to f() in Kotlin files
Searched references to g() in Kotlin files
Searched references to h() in Kotlin files
Used plain search in LocalSearchScope:
CLASS:X
CLASS:Y
CLASS:Z

View File

@@ -0,0 +1,4 @@
Searched inheritors of test.KotlinDataClass
Searched references to test.KotlinDataClass
Used plain search in LocalSearchScope:
CLASS:KotlinDataClass

View File

@@ -58,6 +58,7 @@ import org.jetbrains.kotlin.idea.findUsages.KotlinClassFindUsagesOptions;
import org.jetbrains.kotlin.idea.findUsages.KotlinFindUsagesHandlerFactory;
import org.jetbrains.kotlin.idea.findUsages.KotlinFunctionFindUsagesOptions;
import org.jetbrains.kotlin.idea.findUsages.KotlinPropertyFindUsagesOptions;
import org.jetbrains.kotlin.idea.search.usagesSearch.DestructuringDeclarationUsagesKt;
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase;
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor;
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase;
@@ -69,9 +70,7 @@ import org.jetbrains.kotlin.test.KotlinTestUtils;
import java.io.File;
import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.*;
public abstract class AbstractFindUsagesTest extends KotlinLightCodeInsightFixtureTestCase {
@@ -399,7 +398,22 @@ public abstract class AbstractFindUsagesTest extends KotlinLightCodeInsightFixtu
) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
boolean highlightingMode = InTextDirectivesUtils.isDirectiveDefined(mainFileText, "// HIGHLIGHTING");
Collection<UsageInfo> usageInfos = findUsages(caretElement, options, highlightingMode);
Collection<UsageInfo> usageInfos;
String log = null;
try {
DestructuringDeclarationUsagesKt.setDestructuringDeclarationUsageSearchLog(new ArrayList<String>());
usageInfos = findUsages(caretElement, options, highlightingMode);
}
finally {
List<String> logList = DestructuringDeclarationUsagesKt.getDestructuringDeclarationUsageSearchLog();
assert logList != null;
DestructuringDeclarationUsagesKt.setDestructuringDeclarationUsageSearchLog(null);
if (logList.size() > 0) {
Collections.sort(logList);
log = StringUtil.join(logList, "\n");
}
}
Collection<UsageFilteringRule> filteringRules = instantiateClasses(mainFileText, "// FILTERING_RULES: ");
final Collection<UsageGroupingRule> groupingRules = instantiateClasses(mainFileText, "// GROUPING_RULES: ");
@@ -458,6 +472,10 @@ public abstract class AbstractFindUsagesTest extends KotlinLightCodeInsightFixtu
Collection<String> finalUsages = Ordering.natural().sortedCopy(Collections2.transform(filteredUsages, convertToString));
KotlinTestUtils.assertEqualsToFile(new File(rootPath, prefix + "results.txt"), StringUtil.join(finalUsages, "\n"));
if (log != null) {
KotlinTestUtils.assertEqualsToFile(new File(rootPath, prefix + "log"), log);
}
}
protected Collection<UsageInfo> findUsages(