Add tests for enhanced java signatures based on AbstractResolvedCallsTest.

Refactor AbstractResolvedCallsTest to support multiple carets (multiple methods being tested  for resolve) in testdata file.
This commit is contained in:
Ilya Gorbunov
2016-12-02 21:41:20 +03:00
parent 15061ff125
commit 84a7e3c032
42 changed files with 946 additions and 21 deletions
@@ -0,0 +1,56 @@
/*
* 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.resolve.calls
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.test.ConfigurationKind
import org.jetbrains.kotlin.test.TestJdkKind
abstract class AbstractEnhancedSignaturesResolvedCallsTest : AbstractResolvedCallsTest() {
// requires full JDK with various Java API: java.util.Optional, java.util.Map
override fun createEnvironment(): KotlinCoreEnvironment = createEnvironmentWithJdk(ConfigurationKind.ALL, TestJdkKind.FULL_JDK)
override fun renderOutput(originalText: String, text: String, resolvedCallsAt: List<Pair<Int, ResolvedCall<*>?>>): String {
val lines = text.lines()
val lineOffsets = run {
var offset = 0
lines.map { offset.apply { offset += it.length + 1 /* new-line delimiter */ } }
}
fun lineIndexAt(caret: Int): Int =
lineOffsets.binarySearch(caret).let { result ->
if (result < 0) result.inv() - 1 else result }
val callsByLine = resolvedCallsAt.groupBy ({ (caret) -> lineIndexAt(caret) }, { (_, resolvedCall) -> resolvedCall })
return buildString {
lines.forEachIndexed { lineIndex, line ->
appendln(line)
callsByLine[lineIndex]?.let { calls ->
val indent = line.takeWhile(Char::isWhitespace) + " "
calls.forEach { resolvedCall ->
appendln("$indent// ${resolvedCall?.status}")
appendln("$indent// ORIGINAL: ${resolvedCall?.run { resultingDescriptor!!.original.getText() }}")
appendln("$indent// SUBSTITUTED: ${resolvedCall?.run { resultingDescriptor!!.getText() }}")
}
}
}
}
}
}
@@ -48,34 +48,62 @@ abstract class AbstractResolvedCallsTest : KotlinTestWithEnvironment() {
override fun createEnvironment(): KotlinCoreEnvironment = createEnvironmentWithMockJdk(ConfigurationKind.ALL)
fun doTest(filePath: String) {
val text = KotlinTestUtils.doLoadFile(File(filePath))!!
val originalText = KotlinTestUtils.doLoadFile(File(filePath))!!
val (text, carets) = extractCarets(originalText)
val ktFile = KtPsiFactory(project).createFile(text.replace("<caret>", ""))
val ktFile = KtPsiFactory(project).createFile(text)
val bindingContext = JvmResolveUtil.analyze(ktFile, environment).bindingContext
val (element, cachedCall) = buildCachedCall(bindingContext, ktFile, text)
val resolvedCallsAt = carets.map { caret -> caret to run {
val (element, cachedCall) = buildCachedCallAtIndex(bindingContext, ktFile, caret)
val resolvedCall = if (cachedCall !is VariableAsFunctionResolvedCall) cachedCall
else if ("(" == element?.text) cachedCall.functionCall
else cachedCall.variableCall
val resolvedCall = when {
cachedCall !is VariableAsFunctionResolvedCall -> cachedCall
"(" == element?.text -> cachedCall.functionCall
else -> cachedCall.variableCall
}
resolvedCall
}}
val output = renderOutput(originalText, text, resolvedCallsAt)
val resolvedCallInfoFileName = FileUtil.getNameWithoutExtension(filePath) + ".txt"
KotlinTestUtils.assertEqualsToFile(File(resolvedCallInfoFileName), "$text\n\n\n${resolvedCall?.renderToText()}")
KotlinTestUtils.assertEqualsToFile(File(resolvedCallInfoFileName), output)
}
open protected fun buildCachedCall(
bindingContext: BindingContext, jetFile: KtFile, text: String
protected open fun renderOutput(originalText: String, text: String, resolvedCallsAt: List<Pair<Int, ResolvedCall<*>?>>): String =
resolvedCallsAt.joinToString("\n\n", prefix = "$originalText\n\n\n") { (_, resolvedCall) ->
resolvedCall?.renderToText().toString()
}
protected fun extractCarets(text: String): Pair<String, List<Int>> {
val parts = text.split("<caret>")
if (parts.size < 2) return text to emptyList()
// possible to rewrite using 'scan' function to get partial sums of parts lengths
val indices = mutableListOf<Int>()
val resultText = buildString {
parts.dropLast(1).forEach { part ->
append(part)
indices.add(this.length)
}
append(parts.last())
}
return resultText to indices
}
protected open fun buildCachedCallAtIndex(
bindingContext: BindingContext, jetFile: KtFile, index: Int
): Pair<PsiElement?, ResolvedCall<out CallableDescriptor>?> {
val element = jetFile.findElementAt(text.indexOf("<caret>"))!!
val element = jetFile.findElementAt(index)!!
val expression = element.getStrictParentOfType<KtExpression>()
val cachedCall = expression?.getParentResolvedCall(bindingContext, strict = false)
return Pair(element, cachedCall)
}
}
private fun Receiver?.getText() = when (this) {
internal fun Receiver?.getText() = when (this) {
is ExpressionReceiver -> "${expression.text} {${type}}"
is ImplicitClassReceiver -> "Class{${type}}"
is ExtensionReceiver -> "${type}Ext{${declarationDescriptor.getText()}}"
@@ -83,9 +111,9 @@ private fun Receiver?.getText() = when (this) {
else -> toString()
}
private fun ValueArgument.getText() = this.getArgumentExpression()?.text?.replace("\n", " ") ?: ""
internal fun ValueArgument.getText() = this.getArgumentExpression()?.text?.replace("\n", " ") ?: ""
private fun ArgumentMapping.getText() = when (this) {
internal fun ArgumentMapping.getText() = when (this) {
is ArgumentMatch -> {
val parameterType = DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(valueParameter.type)
"${status.name} ${valueParameter.name} : ${parameterType} ="
@@ -93,12 +121,12 @@ private fun ArgumentMapping.getText() = when (this) {
else -> "ARGUMENT UNMAPPED: "
}
private fun DeclarationDescriptor.getText(): String = when (this) {
internal fun DeclarationDescriptor.getText(): String = when (this) {
is ReceiverParameterDescriptor -> "${value.getText()}::this"
else -> DescriptorRenderer.COMPACT_WITH_SHORT_TYPES.render(this)
}
private fun ResolvedCall<*>.renderToText(): String {
internal fun ResolvedCall<*>.renderToText(): String {
return buildString {
appendln("Resolved call:")
appendln()
@@ -27,10 +27,10 @@ import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
abstract class AbstractResolvedConstructorDelegationCallsTests : AbstractResolvedCallsTest() {
override fun buildCachedCall(
bindingContext: BindingContext, jetFile: KtFile, text: String
override fun buildCachedCallAtIndex(
bindingContext: BindingContext, jetFile: KtFile, index: Int
): Pair<PsiElement?, ResolvedCall<out CallableDescriptor>?> {
val element = jetFile.findElementAt(text.indexOf("<caret>"))
val element = jetFile.findElementAt(index)
val constructor = element?.getNonStrictParentOfType<KtSecondaryConstructor>()!!
val delegationCall = constructor.getDelegationCall()