REPL: Extract IDE-specific tests to a separate test set

This commit is contained in:
Ilya Muradyan
2022-01-25 12:09:27 +03:00
committed by teamcity
parent 9637b6b848
commit 070f537ee9
4 changed files with 188 additions and 158 deletions
@@ -67,4 +67,6 @@ projectTest(taskName = "embeddableTest", parallel = true) {
workingDir = rootDir
dependsOn(embeddableTestRuntime)
classpath = embeddableTestRuntime
exclude("**/JvmReplIdeTest.class")
}
@@ -0,0 +1,75 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.scripting.ide_services
import com.intellij.mock.MockApplication
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.util.indexing.FileContentImpl
import junit.framework.TestCase
import org.jetbrains.kotlin.analysis.decompiler.psi.KotlinClassFileDecompiler
import org.jetbrains.kotlin.analysis.decompiler.stub.file.ClsKotlinBinaryClassCache
import org.jetbrains.kotlin.analysis.decompiler.stub.file.FileAttributeService
import org.jetbrains.kotlin.analysis.decompiler.stub.files.DummyFileAttributeService
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.psi.stubs.KotlinClassStub
import org.jetbrains.kotlin.scripting.compiler.plugin.impl.KJvmCompiledModuleInMemoryImpl
import org.jetbrains.kotlin.scripting.ide_services.test_util.JvmTestRepl
import org.jetbrains.kotlin.scripting.ide_services.test_util.checkCompile
import java.io.File
import kotlin.io.path.ExperimentalPathApi
import kotlin.io.path.createTempDirectory
import kotlin.script.experimental.jvm.impl.KJvmCompiledModuleInMemory
import kotlin.script.experimental.util.get
// This test checks the functionality that works only in IDE
// and doesn't run with embeddableTest configuration
class JvmReplIdeTest : TestCase() {
fun testReplScriptClassFileDecompilation() {
JvmTestRepl()
.use { repl ->
val compiledSnippet = checkCompile(repl, "10 + 10")
val snippetValue = compiledSnippet.get()!!
val compiledModule = snippetValue.getCompiledModule() as KJvmCompiledModuleInMemoryImpl
val folder = saveCompiledOutput("repl-script-decompilation", compiledModule)
val scriptClassName = "Line_0_simplescript"
val fileUrl = "file://" + folder.resolve("$scriptClassName.class").invariantSeparatorsPath
val vFile = VirtualFileManager.getInstance().findFileByUrl(fileUrl)!!
val fileContent = FileContentImpl.createByContent(vFile, vFile.contentsToByteArray(false))
val application = ApplicationManager.getApplication() as MockApplication
KotlinCoreEnvironment.underApplicationLock {
registerDecompilerServices(application)
}
val fileStub = KotlinClassFileDecompiler().stubBuilder.buildFileStub(fileContent)!!
val childrenStubs = fileStub.childrenStubs
assertTrue(childrenStubs.any { it is KotlinClassStub && it.name == scriptClassName })
}
}
@OptIn(ExperimentalPathApi::class)
companion object {
private val outputJarDir = createTempDirectory("temp-ide-services-ide-test")
private fun saveCompiledOutput(subfolder: String, module: KJvmCompiledModuleInMemory): File {
val folder = outputJarDir.resolve(subfolder).toFile()
module.compilerOutputFiles.forEach { (name, contents) ->
val file = folder.resolve(name)
file.parentFile.mkdirs()
file.writeBytes(contents)
}
return folder
}
private fun registerDecompilerServices(application: MockApplication) {
application.registerService(FileAttributeService::class.java, DummyFileAttributeService)
application.registerService(ClsKotlinBinaryClassCache::class.java, ClsKotlinBinaryClassCache())
}
}
}
@@ -5,23 +5,12 @@
package org.jetbrains.kotlin.scripting.ide_services
import com.intellij.mock.MockApplication
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.util.indexing.FileContentImpl
import junit.framework.TestCase
import org.jetbrains.kotlin.analysis.decompiler.psi.KotlinClassFileDecompiler
import org.jetbrains.kotlin.analysis.decompiler.stub.file.ClsKotlinBinaryClassCache
import org.jetbrains.kotlin.analysis.decompiler.stub.file.FileAttributeService
import org.jetbrains.kotlin.analysis.decompiler.stub.files.DummyFileAttributeService
import org.jetbrains.kotlin.cli.common.ExitCode
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocationWithRange
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.config.Services
import org.jetbrains.kotlin.scripting.compiler.plugin.impl.KJvmCompiledModuleInMemoryImpl
import org.jetbrains.kotlin.scripting.ide_services.test_util.*
import java.io.File
import kotlin.io.path.ExperimentalPathApi
@@ -29,15 +18,11 @@ import kotlin.io.path.createTempDirectory
import kotlin.io.path.invariantSeparatorsPathString
import kotlin.reflect.full.isSubclassOf
import kotlin.script.experimental.api.*
import kotlin.script.experimental.jvm.impl.KJvmCompiledModuleInMemory
import kotlin.script.experimental.jvm.impl.KJvmCompiledScript
import kotlin.script.experimental.jvm.jvm
import kotlin.script.experimental.jvm.updateClasspath
import kotlin.script.experimental.jvm.util.isError
import kotlin.script.experimental.jvm.util.isIncomplete
import kotlin.script.experimental.jvm.util.scriptCompilationClasspathFromContext
import kotlin.script.experimental.util.LinkedSnippet
import kotlin.script.experimental.util.get
// Adapted form GenericReplTest
@@ -353,28 +338,6 @@ class JvmIdeServicesTest : TestCase() {
}
}
fun testReplScriptClassFileDecompilation() {
JvmTestRepl()
.use { repl ->
val compiledSnippet = checkCompile(repl, "10 + 10")
val snippetValue = compiledSnippet.get()!!
val compiledModule = snippetValue.getCompiledModule() as KJvmCompiledModuleInMemoryImpl
val folder = saveCompiledOutput("repl-script-decompilation", compiledModule)
val vFile = VirtualFileManager.getInstance().findFileByNioPath(folder.resolve("Line_0_simplescript.class").toPath())!!
val fileContent = FileContentImpl.createByFile(vFile)
val application = ApplicationManager.getApplication() as MockApplication
KotlinCoreEnvironment.underApplicationLock {
registerDecompilerServices(application)
}
val fileStub = KotlinClassFileDecompiler().stubBuilder.buildFileStub(fileContent)
assertNotNull(fileStub)
}
}
@OptIn(ExperimentalPathApi::class)
companion object {
private const val MODULE_PATH = "plugins/scripting/scripting-ide-services-test"
@@ -401,21 +364,6 @@ class JvmIdeServicesTest : TestCase() {
return CliCompilationResult(exitCode, jarPath)
}
private fun saveCompiledOutput(subfolder: String, module: KJvmCompiledModuleInMemory): File {
val folder = outputJarDir.resolve(subfolder).toFile()
module.compilerOutputFiles.forEach { (name, contents) ->
val file = folder.resolve(name)
file.parentFile.mkdirs()
file.writeBytes(contents)
}
return folder
}
private fun registerDecompilerServices(application: MockApplication) {
application.registerService(ClsKotlinBinaryClassCache::class.java)
application.registerService(FileAttributeService::class.java, DummyFileAttributeService)
}
}
}
@@ -472,109 +420,3 @@ class LegacyReplTestLong : TestCase() {
}
}
}
private fun JvmTestRepl.compileAndEval(codeLine: SourceCode): Pair<ResultWithDiagnostics<LinkedSnippet<out CompiledSnippet>>, EvaluatedSnippet?> {
val compRes = compile(codeLine)
val evalRes = compRes.valueOrNull()?.let {
eval(it)
}
return compRes to evalRes?.valueOrNull().get()
}
private fun assertCompileFails(
repl: JvmTestRepl,
@Suppress("SameParameterValue")
line: String
) {
val compiledSnippet =
checkCompile(repl, line)
TestCase.assertNull(compiledSnippet)
}
private fun assertEvalUnit(
repl: JvmTestRepl,
@Suppress("SameParameterValue")
line: String
) {
val compiledSnippet =
checkCompile(repl, line)
val evalResult = repl.eval(compiledSnippet!!)
val valueResult = evalResult.valueOrNull().get()
TestCase.assertNotNull("Unexpected eval result: $evalResult", valueResult)
TestCase.assertTrue(valueResult!!.result is ResultValue.Unit)
}
private fun <R> assertEvalResult(repl: JvmTestRepl, line: String, expectedResult: R) {
val compiledSnippet =
checkCompile(repl, line)
val evalResult = repl.eval(compiledSnippet!!)
val valueResult = evalResult.valueOrNull().get()
TestCase.assertNotNull("Unexpected eval result: $evalResult", valueResult)
TestCase.assertTrue(valueResult!!.result is ResultValue.Value)
TestCase.assertEquals(expectedResult, (valueResult.result as ResultValue.Value).value)
}
private inline fun <reified R> assertEvalResultIs(repl: JvmTestRepl, line: String) {
val compiledSnippet =
checkCompile(repl, line)
val evalResult = repl.eval(compiledSnippet!!)
val valueResult = evalResult.valueOrNull().get()
TestCase.assertNotNull("Unexpected eval result: $evalResult", valueResult)
TestCase.assertTrue(valueResult!!.result is ResultValue.Value)
TestCase.assertTrue((valueResult.result as ResultValue.Value).value is R)
}
private fun checkCompile(repl: JvmTestRepl, line: String): LinkedSnippet<KJvmCompiledScript>? {
val codeLine = repl.nextCodeLine(line)
val compileResult = repl.compile(codeLine)
return compileResult.valueOrNull()
}
private data class CompilationErrors(
val message: String,
val location: CompilerMessageLocationWithRange?
)
private fun <T> ResultWithDiagnostics<T>.getErrors(): CompilationErrors =
CompilationErrors(
reports.joinToString("\n") { report ->
report.location?.let { loc ->
CompilerMessageLocationWithRange.create(
report.sourcePath,
loc.start.line,
loc.start.col,
loc.end?.line,
loc.end?.col,
null
)?.toString()?.let {
"$it "
}
}.orEmpty() + report.message
},
reports.firstOrNull {
when (it.severity) {
ScriptDiagnostic.Severity.ERROR -> true
ScriptDiagnostic.Severity.FATAL -> true
else -> false
}
}?.let {
val loc = it.location ?: return@let null
CompilerMessageLocationWithRange.create(
it.sourcePath,
loc.start.line,
loc.start.col,
loc.end?.line,
loc.end?.col,
null
)
}
)
@@ -5,14 +5,18 @@
package org.jetbrains.kotlin.scripting.ide_services.test_util
import junit.framework.TestCase
import kotlinx.coroutines.runBlocking
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocationWithRange
import org.jetbrains.kotlin.scripting.ide_services.compiler.KJvmReplCompilerWithIdeServices
import java.io.Closeable
import java.util.concurrent.atomic.AtomicInteger
import kotlin.script.experimental.api.*
import kotlin.script.experimental.jvm.BasicJvmReplEvaluator
import kotlin.script.experimental.jvm.impl.KJvmCompiledScript
import kotlin.script.experimental.util.LinkedSnippet
import kotlin.script.experimental.jvm.util.toSourceCodePosition
import kotlin.script.experimental.util.get
internal class JvmTestRepl (
private val compileConfiguration: ScriptCompilationConfiguration = simpleScriptCompilationConfiguration,
@@ -55,3 +59,110 @@ fun <T> ResultWithDiagnostics<Iterable<T>>.toList() = this.valueOrNull()?.toList
@JvmName("sequenceToList")
fun <T> ResultWithDiagnostics<Sequence<T>>.toList() = this.valueOrNull()?.toList().orEmpty()
internal fun JvmTestRepl.compileAndEval(codeLine: SourceCode): Pair<ResultWithDiagnostics<LinkedSnippet<out CompiledSnippet>>, EvaluatedSnippet?> {
val compRes = compile(codeLine)
val evalRes = compRes.valueOrNull()?.let {
eval(it)
}
return compRes to evalRes?.valueOrNull().get()
}
internal fun assertCompileFails(
repl: JvmTestRepl,
@Suppress("SameParameterValue")
line: String
) {
val compiledSnippet =
checkCompile(repl, line)
TestCase.assertNull(compiledSnippet)
}
internal fun assertEvalUnit(
repl: JvmTestRepl,
@Suppress("SameParameterValue")
line: String
) {
val compiledSnippet =
checkCompile(repl, line)
val evalResult = repl.eval(compiledSnippet!!)
val valueResult = evalResult.valueOrNull().get()
TestCase.assertNotNull("Unexpected eval result: $evalResult", valueResult)
TestCase.assertTrue(valueResult!!.result is ResultValue.Unit)
}
internal fun <R> assertEvalResult(repl: JvmTestRepl, line: String, expectedResult: R) {
val compiledSnippet =
checkCompile(repl, line)
val evalResult = repl.eval(compiledSnippet!!)
val valueResult = evalResult.valueOrNull().get()
TestCase.assertNotNull("Unexpected eval result: $evalResult", valueResult)
TestCase.assertTrue(valueResult!!.result is ResultValue.Value)
TestCase.assertEquals(expectedResult, (valueResult.result as ResultValue.Value).value)
}
internal inline fun <reified R> assertEvalResultIs(repl: JvmTestRepl, line: String) {
val compiledSnippet =
checkCompile(repl, line)
val evalResult = repl.eval(compiledSnippet!!)
val valueResult = evalResult.valueOrNull().get()
TestCase.assertNotNull("Unexpected eval result: $evalResult", valueResult)
TestCase.assertTrue(valueResult!!.result is ResultValue.Value)
TestCase.assertTrue((valueResult.result as ResultValue.Value).value is R)
}
internal fun checkCompile(repl: JvmTestRepl, line: String): LinkedSnippet<KJvmCompiledScript>? {
val codeLine = repl.nextCodeLine(line)
val compileResult = repl.compile(codeLine)
return compileResult.valueOrNull()
}
internal data class CompilationErrors(
val message: String,
val location: CompilerMessageLocationWithRange?
)
internal fun <T> ResultWithDiagnostics<T>.getErrors(): CompilationErrors =
CompilationErrors(
reports.joinToString("\n") { report ->
report.location?.let { loc ->
CompilerMessageLocationWithRange.create(
report.sourcePath,
loc.start.line,
loc.start.col,
loc.end?.line,
loc.end?.col,
null
)?.toString()?.let {
"$it "
}
}.orEmpty() + report.message
},
reports.firstOrNull {
when (it.severity) {
ScriptDiagnostic.Severity.ERROR -> true
ScriptDiagnostic.Severity.FATAL -> true
else -> false
}
}?.let {
val loc = it.location ?: return@let null
CompilerMessageLocationWithRange.create(
it.sourcePath,
loc.start.line,
loc.start.col,
loc.end?.line,
loc.end?.col,
null
)
}
)