diff --git a/plugins/scripting/scripting-ide-services-test/build.gradle.kts b/plugins/scripting/scripting-ide-services-test/build.gradle.kts index 283b7afa784..85465d2b79d 100644 --- a/plugins/scripting/scripting-ide-services-test/build.gradle.kts +++ b/plugins/scripting/scripting-ide-services-test/build.gradle.kts @@ -67,4 +67,6 @@ projectTest(taskName = "embeddableTest", parallel = true) { workingDir = rootDir dependsOn(embeddableTestRuntime) classpath = embeddableTestRuntime + + exclude("**/JvmReplIdeTest.class") } diff --git a/plugins/scripting/scripting-ide-services-test/test/org/jetbrains/kotlin/scripting/ide_services/JvmReplIdeTest.kt b/plugins/scripting/scripting-ide-services-test/test/org/jetbrains/kotlin/scripting/ide_services/JvmReplIdeTest.kt new file mode 100644 index 00000000000..7d8357b2b50 --- /dev/null +++ b/plugins/scripting/scripting-ide-services-test/test/org/jetbrains/kotlin/scripting/ide_services/JvmReplIdeTest.kt @@ -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()) + } + } +} \ No newline at end of file diff --git a/plugins/scripting/scripting-ide-services-test/test/org/jetbrains/kotlin/scripting/ide_services/JvmReplTest.kt b/plugins/scripting/scripting-ide-services-test/test/org/jetbrains/kotlin/scripting/ide_services/JvmReplTest.kt index 222200a50db..b9afcd84a31 100644 --- a/plugins/scripting/scripting-ide-services-test/test/org/jetbrains/kotlin/scripting/ide_services/JvmReplTest.kt +++ b/plugins/scripting/scripting-ide-services-test/test/org/jetbrains/kotlin/scripting/ide_services/JvmReplTest.kt @@ -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>, 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 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 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? { - 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 ResultWithDiagnostics.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 - ) - } - ) diff --git a/plugins/scripting/scripting-ide-services-test/test/org/jetbrains/kotlin/scripting/ide_services/test_util/testUtil.kt b/plugins/scripting/scripting-ide-services-test/test/org/jetbrains/kotlin/scripting/ide_services/test_util/testUtil.kt index 37d958f88a5..e9a4be669a2 100644 --- a/plugins/scripting/scripting-ide-services-test/test/org/jetbrains/kotlin/scripting/ide_services/test_util/testUtil.kt +++ b/plugins/scripting/scripting-ide-services-test/test/org/jetbrains/kotlin/scripting/ide_services/test_util/testUtil.kt @@ -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 ResultWithDiagnostics>.toList() = this.valueOrNull()?.toList @JvmName("sequenceToList") fun ResultWithDiagnostics>.toList() = this.valueOrNull()?.toList().orEmpty() + +internal fun JvmTestRepl.compileAndEval(codeLine: SourceCode): Pair>, 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 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 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? { + 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 ResultWithDiagnostics.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 + ) + } + ) +