[JS SCRIPTING] create tests for repl

This commit is contained in:
Vitaliy.Tikhonov
2019-08-30 16:05:05 +03:00
committed by romanart
parent 62885ba497
commit c6c3d2de9e
6 changed files with 429 additions and 0 deletions
@@ -0,0 +1,32 @@
plugins {
kotlin("jvm")
}
jvmTarget = "1.6"
val embeddableTestRuntime by configurations.creating
dependencies {
testCompile(commonDep("junit"))
testCompile(project(":kotlin-scripting-js"))
testCompile(project(":compiler:plugin-api"))
testCompile(project(":kotlin-scripting-compiler"))
testCompile(project(":compiler:cli"))
testCompile(project(":compiler:backend.js"))
testCompile(project(":js:js.engines"))
testCompile(intellijCoreDep()) { includeJars("intellij-core") }
testRuntimeOnly(intellijCoreDep()) { includeJars("intellij-core") }
testRuntimeOnly(intellijDep()) { includeJars("openapi", "idea", "idea_rt", "log4j", "picocontainer-1.2", "guava-25.1-jre", "jdom") }
testRuntimeOnly(commonDep("org.jetbrains.intellij.deps", "trove4j"))
}
sourceSets {
"main" {}
"test" { projectDefault() }
}
projectTest(parallel = true) {
workingDir = rootDir
}
@@ -0,0 +1,80 @@
/*
* Copyright 2010-2019 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.repl.js.test
import com.intellij.openapi.util.Disposer
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.common.repl.ReplCodeLine
import org.jetbrains.kotlin.cli.common.repl.ReplCompileResult
import org.jetbrains.kotlin.cli.common.repl.ReplCompiler
import org.jetbrains.kotlin.cli.common.repl.ReplEvalResult
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.scripting.compiler.plugin.ScriptingCompilerConfigurationComponentRegistrar
import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.platform
import org.jetbrains.kotlin.scripting.repl.js.JsReplEvaluator
import org.jetbrains.kotlin.scripting.repl.js.ReplMessageCollector
import java.io.Closeable
import kotlin.script.experimental.api.ScriptCompilationConfiguration
import kotlin.script.experimental.api.baseClass
import kotlin.script.experimental.api.dependencies
import kotlin.script.experimental.host.ScriptingHostConfiguration
import kotlin.script.experimental.jvm.JsDependency
abstract class AbstractJsReplTest : Closeable {
abstract fun createCompiler(): ReplCompiler
abstract fun preprocessEvaluation()
fun compile(codeLine: ReplCodeLine): ReplCompileResult {
return compiler.compile(compiler.createState(), codeLine)
}
fun evaluate(compileResult: ReplCompileResult.CompiledClasses): ReplEvalResult {
return jsEvaluator.eval(jsEvaluator.createState(), compileResult)
}
fun reset() {
collector.clear()
compiler = createCompiler()
jsEvaluator = JsReplEvaluator()
preprocessEvaluation()
}
private val collector: MessageCollector = ReplMessageCollector()
protected val disposable = Disposer.newDisposable()
protected val environment = KotlinCoreEnvironment.createForProduction(
disposable, loadConfiguration(), EnvironmentConfigFiles.JS_CONFIG_FILES
)
lateinit var compiler: ReplCompiler
lateinit var jsEvaluator: JsReplEvaluator
private var snippetId: Int = 1 //index 0 for klib
fun newSnippetId(): Int = snippetId++
private fun loadConfiguration(): CompilerConfiguration {
val configuration = CompilerConfiguration()
configuration.add(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS, ScriptingCompilerConfigurationComponentRegistrar())
configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, collector)
configuration.put(CommonConfigurationKeys.MODULE_NAME, "repl.kts")
val scriptConfiguration = ScriptCompilationConfiguration {
baseClass("kotlin.Any")
dependencies.append(JsDependency("compiler/ir/serialization.js/build/fullRuntime/klib"))
platform.put("JS")
}
configuration.add(
ScriptingConfigurationKeys.SCRIPT_DEFINITIONS,
ScriptDefinition.FromConfigurations(ScriptingHostConfiguration(), scriptConfiguration, null)
)
return configuration
}
}
@@ -0,0 +1,44 @@
/*
* Copyright 2010-2019 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.repl.js.test
import org.jetbrains.kotlin.cli.common.repl.ReplCompiler
import org.jetbrains.kotlin.ir.backend.js.utils.NameTables
import org.jetbrains.kotlin.scripting.repl.js.*
class JsReplTestAgainstBinaries : AbstractJsReplTest() {
private val dependencyLoader = DependencyLoader()
private val runtimeBinary: String
private val nameTable: NameTables
init {
val dependencies = readLibrariesFromConfiguration(environment.configuration)
val compiler = ScriptDependencyCompiler(environment)
val result = compiler.compile(dependencies)
runtimeBinary = result.first
nameTable = result.second
dependencyLoader.saveScriptDependencyBinary(runtimeBinary)
dependencyLoader.saveNames(nameTable)
}
override fun createCompiler(): ReplCompiler {
return JsDebuggerCompiler(environment, dependencyLoader.loadNames())
}
override fun preprocessEvaluation() {
jsEvaluator.eval(
jsEvaluator.createState(),
createCompileResult(dependencyLoader.loadScriptDependencyBinary())
)
}
override fun close() {
//do nothing
}
}
@@ -0,0 +1,27 @@
/*
* Copyright 2010-2019 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.repl.js.test
import com.intellij.openapi.util.Disposer
import org.jetbrains.kotlin.cli.common.repl.ReplCompiler
import org.jetbrains.kotlin.scripting.repl.js.*
class JsReplTestAgainstKlib : AbstractJsReplTest() {
override fun createCompiler(): ReplCompiler = JsReplCompiler(environment)
override fun preprocessEvaluation() {
val scriptDependencyBinary = (compiler as JsReplCompiler).scriptDependencyBinary
jsEvaluator.eval(
jsEvaluator.createState(),
createCompileResult(scriptDependencyBinary)
)
}
override fun close() {
Disposer.dispose(disposable)
}
}
@@ -0,0 +1,243 @@
/*
* Copyright 2010-2019 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.repl.js.test
import junit.framework.TestCase
import org.jetbrains.kotlin.cli.common.repl.ReplCompileResult
import org.junit.Assert
import org.junit.Test
import org.jetbrains.kotlin.cli.common.repl.ReplEvalResult
import org.jetbrains.kotlin.scripting.repl.js.makeReplCodeLine
abstract class AbstractReplTestRunner : TestCase() {
abstract fun getTester(): AbstractJsReplTest
@Test
fun testIndependentLines() {
val lines = listOf(
"var x = 38",
"var y = 99",
"4 + 1"
)
Assert.assertEquals(5, compileAndEval(lines))
}
@Test
fun testDependentLines() {
val lines = listOf(
"var x = 7",
"var y = 32",
"x + y"
)
Assert.assertEquals(39, compileAndEval(lines))
}
@Test
fun testFunctionCall() {
val lines = listOf(
"var x = 2",
"var y = 3",
"fun foo(x: Int, unused: Int) = x + y",
"foo(x, x) * foo(y, y)"
)
Assert.assertEquals(30, compileAndEval(lines))
}
@Test
fun testList() {
val lines = listOf(
"var a = 4",
"var b = 6",
"listOf(a, 5, b).last()"
)
Assert.assertEquals(6, compileAndEval(lines))
}
@Test
fun testMatchingNames() {
val lines = listOf(
"fun foo(i: Int) = i + 2",
"fun foo(s: String) = s",
"class C {fun foo(s: String) = s + s}",
"foo(\"x\") + foo(2) + C().foo(\"class\")"
)
Assert.assertEquals("x4classclass", compileAndEval(lines))
}
@Test
fun testInline() {
val lines = listOf(
"inline fun foo(i : Int) = if (i % 2 == 0) {} else i",
"""
fun box(): String {
val a = foo(1)
if (a != 1) return "fail1: ${'$'}a"
val b = foo(2)
if (b != Unit) return "fail2: ${'$'}b"
return "OK"
},
box()
"""
)
Assert.assertEquals("OK", compileAndEval(lines))
}
@Test
fun testAnonymous() {
val lines = listOf(
"""
inline fun foo(f: () -> String): () -> String {
val result = f()
return { result }
}
""",
"fun bar(f: () -> String) = foo(f)()",
"fun box(): String = bar { \"OK\" }",
"box()"
)
Assert.assertEquals("OK", compileAndEval(lines))
}
@Test
fun testNoneLocalReturn() {
val lines = listOf(
"""
inline fun f(ignored: () -> Any): Any {
return ignored()
}
""",
"""
fun test(): String {
f { return "OK" };
return "error"
}
""",
"test()"
)
Assert.assertEquals("OK", compileAndEval(lines))
}
@Test
fun testInstanceOf() {
val lines = listOf(
"""
val list = listOf(1, 2, 3)
val f: Boolean = list is List<Int>
""",
"val s = list is List<Int>",
"f.toString() + s.toString()"
)
Assert.assertEquals("truetrue", compileAndEval(lines))
}
@Test
fun testScopes() {
val lines = listOf(
"""
fun foo(): Int {
var t = 2 * 2
class A(val value: Int = 5) {
fun bar(): Int {
var q = 4
var w = 1
return q + w
}
}
return A().bar() * 2
}
foo()
"""
)
Assert.assertEquals(10, compileAndEval(lines))
}
@Test
fun testEvaluateFunctionName() {
val lines = listOf(
"fun evaluateScript() = 5",
"fun foo(i: Int) = i + evaluateScript()",
"foo(5)"
)
Assert.assertEquals(10, compileAndEval(lines))
}
@Test
fun testMemberDeclarations() {
val lines = listOf(
"""
val a = listOf(1, 2, 3, 4, 5)
val str = "" + kotlin.math.PI
str + a.subList(2, 3).toString() + a.lastIndexOf(4)
"""
)
Assert.assertEquals("3.141592653589793[3]3", compileAndEval(lines))
}
@Test
fun testInitializeScriptFunction() {
val lines = listOf(
"""
var result = ""
class Class(val x: Int = 10)
result += Class().x
fun function() = "#$@"
result += function()
val field = 123456
result += field
val sq: (x: Int) -> Int = { x -> x * x }
result += "_" + sq(9)
result += if (sq(5) % 2 == 0) {
class I(val x: Int = 100)
I().x
} else {
class I(val x: String = "goo")
I().x
}
result
"""
)
Assert.assertEquals("10#$@123456_81goo", compileAndEval(lines))
}
private fun compileAndEval(lines: List<String>): Any? {
var result: Any? = null
getTester().use { tester ->
tester.reset()
lines.forEach { line ->
val compileResult = tester.compile(makeReplCodeLine(tester.newSnippetId(), line))
if (compileResult !is ReplCompileResult.CompiledClasses) return compileResult.toString()
val evalResult = tester.evaluate(compileResult)
if (evalResult !is ReplEvalResult.ValueResult) return evalResult.toString()
result = evalResult.value
}
}
return result
}
}
class ReplTestRunnerAgainstKLib : AbstractReplTestRunner() {
override fun getTester(): AbstractJsReplTest = JsReplTestAgainstKlib()
}
class ReplTestRunnerAgainstBinaries : AbstractReplTestRunner() {
override fun getTester(): AbstractJsReplTest = tester
companion object {
private val tester = JsReplTestAgainstBinaries()
}
}
@@ -149,6 +149,9 @@ abstract class ScriptDefinition : UserDataHolderBase() {
?: hostConfiguration[ScriptingHostConfiguration.jvm.baseClassLoader]
}
override val platform: String
get() = compilationConfiguration[ScriptCompilationConfiguration.platform] ?: super.platform
override val baseClassType: KotlinType
get() = compilationConfiguration[ScriptCompilationConfiguration.baseClass]!!