Add tests for analyze return type in REPL IDE services

also refactor existing tests infrastructure
This commit is contained in:
Ilya Muradyan
2020-05-06 04:55:47 +03:00
committed by Ilya Chernikov
parent 8a1b44cc2b
commit 7cde9b4355
@@ -14,6 +14,8 @@ import org.jetbrains.kotlin.scripting.ide_services.test_util.toList
import org.junit.Assert import org.junit.Assert
import org.junit.Test import org.junit.Test
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
import kotlin.reflect.KProperty
import kotlin.reflect.KProperty0
import kotlin.script.experimental.api.* import kotlin.script.experimental.api.*
class ReplCompletionAndErrorsAnalysisTest : TestCase() { class ReplCompletionAndErrorsAnalysisTest : TestCase() {
@@ -43,7 +45,7 @@ class ReplCompletionAndErrorsAnalysisTest : TestCase() {
code = "v.mema." code = "v.mema."
cursor = 7 cursor = 7
expect { expect {
completionsMode(ComparisonType.INCLUDES) completions.mode(ComparisonType.INCLUDES)
addCompletion("memx", "memx", "Int", "property") addCompletion("memx", "memx", "Int", "property")
addCompletion("memy", "memy", "String", "property") addCompletion("memy", "memy", "String", "property")
} }
@@ -69,7 +71,7 @@ class ReplCompletionAndErrorsAnalysisTest : TestCase() {
code = testCode code = testCode
cursor = testCursor ?: testCode.length cursor = testCursor ?: testCode.length
expect { expect {
completionsMode(ComparisonType.EQUALS) completions.mode(ComparisonType.EQUALS)
} }
} }
@@ -97,7 +99,7 @@ class ReplCompletionAndErrorsAnalysisTest : TestCase() {
cursor = 17 cursor = 17
code = "import java.lang." code = "import java.lang."
expect { expect {
completionsMode(ComparisonType.INCLUDES) completions.mode(ComparisonType.INCLUDES)
addCompletion("Process", "Process", " (java.lang)", "class") addCompletion("Process", "Process", " (java.lang)", "class")
} }
} }
@@ -173,7 +175,7 @@ class ReplCompletionAndErrorsAnalysisTest : TestCase() {
val v = BClass("KKK", AClass(5, "25")) val v = BClass("KKK", AClass(5, "25"))
""".trimIndent() """.trimIndent()
expect { expect {
errorsMode(ComparisonType.EQUALS) errors.mode(ComparisonType.EQUALS)
} }
} }
@@ -214,7 +216,21 @@ class ReplCompletionAndErrorsAnalysisTest : TestCase() {
} }
} }
private class TestConf { @Test
fun testAnalyze() = test {
run {
code = """
val foo = 42
foo
""".trimIndent()
expect {
errors.mode(ComparisonType.EQUALS)
resultType = "Int"
}
}
}
class TestConf {
private val runs = mutableListOf<Run>() private val runs = mutableListOf<Run>()
fun run(setup: (Run).() -> Unit) { fun run(setup: (Run).() -> Unit) {
@@ -258,29 +274,13 @@ class ReplCompletionAndErrorsAnalysisTest : TestCase() {
} }
class Expected(private val run: Run) { class Expected(private val run: Run) {
private val variants = mutableListOf<SourceCodeCompletionVariant>() val completions = ExpectedList<SourceCodeCompletionVariant>(run::doComplete)
private var _completionsMode: ComparisonType = ComparisonType.DONT_CHECK
fun completionsMode(mode: ComparisonType) {
_completionsMode = mode
}
fun addCompletion(text: String, displayText: String, tail: String, icon: String) { fun addCompletion(text: String, displayText: String, tail: String, icon: String) {
if (_completionsMode == ComparisonType.DONT_CHECK) completions.add(SourceCodeCompletionVariant(text, displayText, tail, icon))
_completionsMode = ComparisonType.EQUALS
run.doComplete
variants.add(SourceCodeCompletionVariant(text, displayText, tail, icon))
}
private val errors = mutableListOf<ScriptDiagnostic>()
private var _errorsMode: ComparisonType = ComparisonType.DONT_CHECK
fun errorsMode(mode: ComparisonType) {
_errorsMode = mode
} }
val errors = ExpectedList<ScriptDiagnostic>(run::doErrorCheck)
fun addError(startLine: Int, startCol: Int, endLine: Int, endCol: Int, message: String, severity: String) { fun addError(startLine: Int, startCol: Int, endLine: Int, endCol: Int, message: String, severity: String) {
if (_errorsMode == ComparisonType.DONT_CHECK)
_errorsMode = ComparisonType.EQUALS
run.doErrorCheck
errors.add( errors.add(
ScriptDiagnostic( ScriptDiagnostic(
ScriptDiagnostic.unspecifiedError, ScriptDiagnostic.unspecifiedError,
@@ -294,8 +294,10 @@ class ReplCompletionAndErrorsAnalysisTest : TestCase() {
) )
} }
var resultType: String? by ExpectedNullableVar(run::doErrorCheck)
fun collect(): ExpectedResult { fun collect(): ExpectedResult {
return ExpectedResult(variants, _completionsMode, errors, _errorsMode) return ExpectedResult(completions, errors, resultType)
} }
} }
@@ -317,12 +319,46 @@ class ReplCompletionAndErrorsAnalysisTest : TestCase() {
val code: String, val code: String,
val doCompile: Boolean, val doCompile: Boolean,
val doComplete: Boolean, val doComplete: Boolean,
val doErrorCheck: Boolean val doErrorCheck: Boolean,
) )
class ExpectedList<T>(private val runProperty: KProperty0<Unit>) {
val list = mutableListOf<T>()
private var _compMode = ComparisonType.DONT_CHECK
fun mode() = _compMode
fun mode(mode: ComparisonType) {
_compMode = mode
}
fun add(elem: T) {
if (_compMode == ComparisonType.DONT_CHECK)
_compMode = ComparisonType.EQUALS
runProperty.get()
list.add(elem)
}
}
class ExpectedNullableVar<T>(private val runProperty: KProperty0<Unit>) {
private var variable: T? = null
operator fun getValue(thisRef: Any?, property: KProperty<*>): T? = variable
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T?) {
runProperty.get()
variable = value
}
}
data class ExpectedResult( data class ExpectedResult(
val completions: List<SourceCodeCompletionVariant>, val completionsCompType: ComparisonType, val completions: ExpectedList<SourceCodeCompletionVariant>,
val errors: List<ScriptDiagnostic>, val errorsCompType: ComparisonType val errors: ExpectedList<ScriptDiagnostic>,
val resultType: String?,
)
data class ActualResult(
val completions: List<SourceCodeCompletionVariant>,
val errors: List<ScriptDiagnostic>,
val resultType: String?,
) )
private val currentLineCounter = AtomicInteger() private val currentLineCounter = AtomicInteger()
@@ -336,9 +372,9 @@ class ReplCompletionAndErrorsAnalysisTest : TestCase() {
private suspend fun evaluateInRepl( private suspend fun evaluateInRepl(
compilationConfiguration: ScriptCompilationConfiguration, compilationConfiguration: ScriptCompilationConfiguration,
snippets: List<RunRequest> snippets: List<RunRequest>
): List<ResultWithDiagnostics<Pair<List<SourceCodeCompletionVariant>, List<ScriptDiagnostic>>>> { ): List<ResultWithDiagnostics<ActualResult>> {
val compiler = KJvmReplCompilerWithIdeServices() val compiler = KJvmReplCompilerWithIdeServices()
return snippets.map { (cursor, snippetText, doComplile, doComplete, doErrorCheck) -> return snippets.map { (cursor, snippetText, doCompile, doComplete, doErrorCheck) ->
val pos = SourceCode.Position(0, 0, cursor) val pos = SourceCode.Position(0, 0, cursor)
val codeLine = nextCodeLine(snippetText) val codeLine = nextCodeLine(snippetText)
val completionRes = if (doComplete && cursor != null) { val completionRes = if (doComplete && cursor != null) {
@@ -348,19 +384,22 @@ class ReplCompletionAndErrorsAnalysisTest : TestCase() {
emptyList() emptyList()
} }
val errorsRes = if (doErrorCheck) { val analysisResult = if (doErrorCheck) {
val codeLineForErrorCheck = nextCodeLine(snippetText) val codeLineForErrorCheck = nextCodeLine(snippetText)
compiler.analyze(codeLineForErrorCheck, SourceCode.Position(0, 0), compilationConfiguration).toList() compiler.analyze(codeLineForErrorCheck, SourceCode.Position(0, 0), compilationConfiguration).valueOrNull()
} else { } else {
emptyList() null
} }?: ReplAnalyzerResult()
if (doComplile) { val errorsSequence = analysisResult[ReplAnalyzerResult.analysisDiagnostics]!!
val resultType = analysisResult[ReplAnalyzerResult.renderedResultType]
if (doCompile) {
val codeLineForCompilation = nextCodeLine(snippetText) val codeLineForCompilation = nextCodeLine(snippetText)
compiler.compile(codeLineForCompilation, compilationConfiguration) compiler.compile(codeLineForCompilation, compilationConfiguration)
} }
Pair(completionRes, errorsRes).asSuccess() ActualResult(completionRes, errorsSequence.toList(), resultType).asSuccess()
} }
} }
@@ -390,14 +429,15 @@ class ReplCompletionAndErrorsAnalysisTest : TestCase() {
when (res) { when (res) {
is ResultWithDiagnostics.Failure -> Assert.fail("#$index: Expected result, got $res") is ResultWithDiagnostics.Failure -> Assert.fail("#$index: Expected result, got $res")
is ResultWithDiagnostics.Success -> { is ResultWithDiagnostics.Success -> {
val (expectedCompletions, completionsCompType, expectedErrors, errorsCompType) = expectedIter.next() val (expectedCompletions, expectedErrors, expectedResultType) = expectedIter.next()
val (completionsRes, errorsRes) = res.value val (completionsRes, errorsRes, resultType) = res.value
checkLists(index, "completions", expectedCompletions, completionsRes, completionsCompType) checkLists(index, "completions", expectedCompletions.list, completionsRes, expectedCompletions.mode())
val expectedErrorsWithPath = expectedErrors.map { val expectedErrorsWithPath = expectedErrors.list.map {
it.copy(sourcePath = errorsRes.firstOrNull()?.sourcePath) it.copy(sourcePath = errorsRes.firstOrNull()?.sourcePath)
} }
checkLists(index, "errors", expectedErrorsWithPath, errorsRes, errorsCompType) checkLists(index, "errors", expectedErrorsWithPath, errorsRes, expectedErrors.mode())
assertEquals("Analysis result types are different", expectedResultType, resultType)
} }
} }
} }