Move compiler scripting tests to scripting plugin, remove unused funs

also remove some tests that are covered in the scripting-compiler
tests now.
Part of the cleanup to rewrite scripting to the new infrastructure.
This commit is contained in:
Ilya Chernikov
2020-04-03 23:02:40 +02:00
parent cf387ffad1
commit d863dc04e6
49 changed files with 334 additions and 279 deletions
@@ -35,7 +35,6 @@ import org.jetbrains.kotlin.backend.common.serialization.signature.IdSignatureDe
import org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory import org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory
import org.jetbrains.kotlin.backend.jvm.jvmPhases import org.jetbrains.kotlin.backend.jvm.jvmPhases
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.ExitCode
import org.jetbrains.kotlin.cli.common.checkKotlinPackageUsage import org.jetbrains.kotlin.cli.common.checkKotlinPackageUsage
import org.jetbrains.kotlin.cli.common.config.addKotlinSourceRoot import org.jetbrains.kotlin.cli.common.config.addKotlinSourceRoot
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
@@ -76,9 +75,7 @@ import org.jetbrains.kotlin.resolve.PlatformDependentAnalyzerServices
import org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade import org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatformAnalyzerServices import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatformAnalyzerServices
import org.jetbrains.kotlin.utils.newLinkedHashMapWithExpectedSize import org.jetbrains.kotlin.utils.newLinkedHashMapWithExpectedSize
import org.jetbrains.kotlin.utils.tryConstructClassFromStringArgs
import java.io.File import java.io.File
import java.lang.reflect.InvocationTargetException
import java.net.URLClassLoader import java.net.URLClassLoader
object KotlinToJVMBytecodeCompiler { object KotlinToJVMBytecodeCompiler {
@@ -443,26 +440,6 @@ object KotlinToJVMBytecodeCompiler {
} }
} }
fun compileAndExecuteScript(environment: KotlinCoreEnvironment, scriptArgs: List<String>): ExitCode {
val scriptClass = compileScript(environment) ?: return ExitCode.COMPILATION_ERROR
try {
try {
tryConstructClassFromStringArgs(scriptClass, scriptArgs)
?: throw RuntimeException("unable to find appropriate constructor for class ${scriptClass.name} accepting arguments $scriptArgs\n")
} finally {
// NB: these lines are required (see KT-9546) but aren't covered by tests
System.out.flush()
System.err.flush()
}
} catch (e: Throwable) {
reportExceptionFromScript(e)
return ExitCode.SCRIPT_EXECUTION_ERROR
}
return ExitCode.OK
}
private fun repeatAnalysisIfNeeded( private fun repeatAnalysisIfNeeded(
result: AnalysisResult?, result: AnalysisResult?,
environment: KotlinCoreEnvironment, environment: KotlinCoreEnvironment,
@@ -496,42 +473,6 @@ object KotlinToJVMBytecodeCompiler {
return result return result
} }
private fun reportExceptionFromScript(exception: Throwable) {
// expecting InvocationTargetException from constructor invocation with cause that describes the actual cause
val stream = System.err
val cause = exception.cause
if (exception !is InvocationTargetException || cause == null) {
exception.printStackTrace(stream)
return
}
stream.println(cause)
val fullTrace = cause.stackTrace
for (i in 0 until fullTrace.size - exception.stackTrace.size) {
stream.println("\tat " + fullTrace[i])
}
}
fun compileScript(environment: KotlinCoreEnvironment, parentClassLoader: ClassLoader? = null): Class<*>? {
val state = analyzeAndGenerate(environment) ?: return null
try {
val urls = environment.configuration.getList(CLIConfigurationKeys.CONTENT_ROOTS).mapNotNull { root ->
when (root) {
is JvmModulePathRoot -> root.file // TODO: only add required modules
is JvmClasspathRoot -> root.file
else -> null
}
}.map { it.toURI().toURL() }
val classLoader = GeneratedClassLoader(state.factory, parentClassLoader ?: URLClassLoader(urls.toTypedArray(), null))
val script = environment.getSourceFiles()[0].script ?: error("Script must be parsed")
return classLoader.loadClass(script.fqName.asString())
} catch (e: Exception) {
throw RuntimeException("Failed to evaluate script: $e", e)
}
}
@Suppress("MemberVisibilityCanBePrivate") // Used in ExecuteKotlinScriptMojo @Suppress("MemberVisibilityCanBePrivate") // Used in ExecuteKotlinScriptMojo
fun analyzeAndGenerate(environment: KotlinCoreEnvironment): GenerationState? { fun analyzeAndGenerate(environment: KotlinCoreEnvironment): GenerationState? {
val result = repeatAnalysisIfNeeded(analyze(environment, null), environment, null) ?: return null val result = repeatAnalysisIfNeeded(analyze(environment, null), environment, null) ?: return null
@@ -28,7 +28,6 @@ import org.jetbrains.kotlin.script.loadScriptingPlugin
import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import org.jetbrains.kotlin.scripting.resolve.KotlinScriptDefinitionFromAnnotatedTemplate import org.jetbrains.kotlin.scripting.resolve.KotlinScriptDefinitionFromAnnotatedTemplate
import org.jetbrains.kotlin.scripts.TestKotlinScriptDependenciesResolver
import org.jetbrains.kotlin.test.ConfigurationKind import org.jetbrains.kotlin.test.ConfigurationKind
import org.jetbrains.kotlin.test.KotlinTestUtils import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.test.TestJdkKind import org.jetbrains.kotlin.test.TestJdkKind
@@ -145,8 +144,5 @@ class ScriptGenTest : CodegenTestCase() {
} }
@Suppress("unused") @Suppress("unused")
@ScriptTemplateDefinition( @ScriptTemplateDefinition(scriptFilePattern = ".*\\.lang\\.kts")
scriptFilePattern = ".*\\.lang\\.kts",
resolver = TestKotlinScriptDependenciesResolver::class
)
abstract class ScriptWithIntParam(val num: Int) abstract class ScriptWithIntParam(val num: Int)
@@ -28,11 +28,12 @@ import org.jetbrains.kotlin.daemon.client.DaemonReportingTargets
import org.jetbrains.kotlin.daemon.client.KotlinCompilerClient import org.jetbrains.kotlin.daemon.client.KotlinCompilerClient
import org.jetbrains.kotlin.daemon.common.* import org.jetbrains.kotlin.daemon.common.*
import org.jetbrains.kotlin.integration.KotlinIntegrationTestBase import org.jetbrains.kotlin.integration.KotlinIntegrationTestBase
import org.jetbrains.kotlin.scripts.captureOut
import org.jetbrains.kotlin.test.KotlinTestUtils import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.test.testFramework.resetApplicationToNull import org.jetbrains.kotlin.test.testFramework.resetApplicationToNull
import org.junit.Assert import org.junit.Assert
import java.io.ByteArrayOutputStream
import java.io.File import java.io.File
import java.io.PrintStream
import java.net.URLClassLoader import java.net.URLClassLoader
class CompilerApiTest : KotlinIntegrationTestBase() { class CompilerApiTest : KotlinIntegrationTestBase() {
@@ -204,3 +205,16 @@ fun TestMessageCollector.assertHasMessage(msg: String, desiredSeverity: Compiler
} }
} }
internal fun captureOut(body: () -> Unit): String {
val outStream = ByteArrayOutputStream()
val prevOut = System.out
System.setOut(PrintStream(outStream))
try {
body()
} finally {
System.out.flush()
System.setOut(prevOut)
}
return outStream.toString()
}
@@ -14,13 +14,13 @@ import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.common.messages.OutputMessageUtil import org.jetbrains.kotlin.cli.common.messages.OutputMessageUtil
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler
import org.jetbrains.kotlin.config.Services import org.jetbrains.kotlin.config.Services
import org.jetbrains.kotlin.daemon.captureOut
import org.jetbrains.kotlin.daemon.client.DaemonReportingTargets import org.jetbrains.kotlin.daemon.client.DaemonReportingTargets
import org.jetbrains.kotlin.daemon.client.KotlinCompilerDaemonClient import org.jetbrains.kotlin.daemon.client.KotlinCompilerDaemonClient
import org.jetbrains.kotlin.daemon.common.* import org.jetbrains.kotlin.daemon.common.*
import org.jetbrains.kotlin.daemon.common.ReportSeverity import org.jetbrains.kotlin.daemon.common.ReportSeverity
import org.jetbrains.kotlin.daemon.loggerCompatiblePath import org.jetbrains.kotlin.daemon.loggerCompatiblePath
import org.jetbrains.kotlin.integration.KotlinIntegrationTestBase import org.jetbrains.kotlin.integration.KotlinIntegrationTestBase
import org.jetbrains.kotlin.scripts.captureOut
import org.jetbrains.kotlin.test.IgnoreAll import org.jetbrains.kotlin.test.IgnoreAll
import org.jetbrains.kotlin.test.KotlinTestUtils import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.test.testFramework.resetApplicationToNull import org.jetbrains.kotlin.test.testFramework.resetApplicationToNull
@@ -1,56 +0,0 @@
/*
* 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.scripts
import org.junit.Assert
import java.io.ByteArrayOutputStream
import java.io.PrintStream
internal const val NUM_4_LINE = "num: 4"
internal const val FIB_SCRIPT_OUTPUT_TAIL =
"""
fib(1)=1
fib(0)=1
fib(2)=2
fib(1)=1
fib(3)=3
fib(1)=1
fib(0)=1
fib(2)=2
fib(4)=5
"""
internal fun captureOut(body: () -> Unit): String {
val outStream = ByteArrayOutputStream()
val prevOut = System.out
System.setOut(PrintStream(outStream))
try {
body()
}
finally {
System.out.flush()
System.setOut(prevOut)
}
return outStream.toString()
}
private fun String.linesSplitTrim() =
split('\n','\r').map(String::trim).filter(String::isNotBlank)
internal fun assertEqualsTrimmed(expected: String, actual: String) =
Assert.assertEquals(expected.linesSplitTrim(), actual.linesSplitTrim())
@@ -17,12 +17,12 @@
package org.jetbrains.kotlin.script.util package org.jetbrains.kotlin.script.util
import com.intellij.openapi.util.Disposer import com.intellij.openapi.util.Disposer
import kotlinx.coroutines.runBlocking
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.config.addKotlinSourceRoot import org.jetbrains.kotlin.cli.common.config.addKotlinSourceRoot
import org.jetbrains.kotlin.cli.common.messages.* import org.jetbrains.kotlin.cli.common.messages.*
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler
import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoot import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoot
import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots
import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots
@@ -35,9 +35,12 @@ import org.jetbrains.kotlin.script.util.templates.BindingsScriptTemplateWithLoca
import org.jetbrains.kotlin.script.util.templates.StandardArgsScriptTemplateWithLocalResolving import org.jetbrains.kotlin.script.util.templates.StandardArgsScriptTemplateWithLocalResolving
import org.jetbrains.kotlin.script.util.templates.StandardArgsScriptTemplateWithMavenResolving import org.jetbrains.kotlin.script.util.templates.StandardArgsScriptTemplateWithMavenResolving
import org.jetbrains.kotlin.scripting.compiler.plugin.ScriptingCompilerConfigurationComponentRegistrar import org.jetbrains.kotlin.scripting.compiler.plugin.ScriptingCompilerConfigurationComponentRegistrar
import org.jetbrains.kotlin.scripting.compiler.plugin.impl.ScriptJvmCompilerFromEnvironment
import org.jetbrains.kotlin.scripting.compiler.plugin.toCompilerMessageSeverity
import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionProvider
import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition
import org.jetbrains.kotlin.scripting.resolve.KotlinScriptDefinitionFromAnnotatedTemplate import org.jetbrains.kotlin.scripting.resolve.KotlinScriptDefinitionFromAnnotatedTemplate
import org.jetbrains.kotlin.utils.PathUtil.getResourcePathForClass import org.jetbrains.kotlin.utils.PathUtil.getResourcePathForClass
@@ -45,9 +48,13 @@ import org.junit.Assert
import org.junit.Ignore import org.junit.Ignore
import org.junit.Test import org.junit.Test
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.io.File
import java.io.OutputStream import java.io.OutputStream
import java.io.PrintStream import java.io.PrintStream
import kotlin.reflect.KClass import kotlin.reflect.KClass
import kotlin.script.experimental.api.onSuccess
import kotlin.script.experimental.api.valueOr
import kotlin.script.experimental.host.toScriptSource
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
const val KOTLIN_JAVA_RUNTIME_JAR = "kotlin-stdlib.jar" const val KOTLIN_JAVA_RUNTIME_JAR = "kotlin-stdlib.jar"
@@ -180,8 +187,22 @@ done
} }
messageCollector.report(CompilerMessageSeverity.LOGGING, "compilation classpath:\n ${environment.configuration.jvmClasspathRoots.joinToString("\n ")}") messageCollector.report(CompilerMessageSeverity.LOGGING, "compilation classpath:\n ${environment.configuration.jvmClasspathRoots.joinToString("\n ")}")
val scriptCompiler = ScriptJvmCompilerFromEnvironment(environment)
return try { return try {
KotlinToJVMBytecodeCompiler.compileScript(environment) val script = File(scriptPath).toScriptSource()
val newScriptDefinition = ScriptDefinitionProvider.getInstance(environment.project)!!.findDefinition(script)!!
val compiledScript = scriptCompiler.compile(script, newScriptDefinition.compilationConfiguration).onSuccess {
runBlocking {
it.getClass(newScriptDefinition.evaluationConfiguration)
}
}.valueOr {
for (report in it.reports) {
messageCollector.report(report.severity.toCompilerMessageSeverity(), report.render(withSeverity = false))
}
return null
}
compiledScript.java
} catch (e: CompilationException) { } catch (e: CompilationException) {
messageCollector.report( messageCollector.report(
CompilerMessageSeverity.EXCEPTION, OutputMessageUtil.renderException(e), CompilerMessageSeverity.EXCEPTION, OutputMessageUtil.renderException(e),
@@ -7,6 +7,8 @@ plugins {
} }
dependencies { dependencies {
testRuntimeOnly(intellijDep()) // Should come before compiler, because of "progarded" stuff needed for tests
compileOnly(project(":compiler:frontend")) compileOnly(project(":compiler:frontend"))
compileOnly(project(":compiler:frontend.java")) compileOnly(project(":compiler:frontend.java"))
compileOnly(project(":compiler:psi")) compileOnly(project(":compiler:psi"))
@@ -32,8 +34,7 @@ dependencies {
testCompile(projectTests(":compiler:tests-common")) testCompile(projectTests(":compiler:tests-common"))
testCompile(commonDep("junit:junit")) testCompile(commonDep("junit:junit"))
testRuntimeOnly(intellijCoreDep()) { includeJars("intellij-core") } testImplementation(intellijCoreDep()) { includeJars("intellij-core") }
testRuntimeOnly(intellijDep()) { includeJars("jps-model") }
} }
sourceSets { sourceSets {
@@ -60,4 +61,5 @@ testsJar()
projectTest { projectTest {
dependsOn(":dist") dependsOn(":dist")
workingDir = rootDir workingDir = rootDir
systemProperty("kotlin.test.script.classpath", testSourceSet.output.classesDirs.joinToString(File.pathSeparator))
} }
@@ -141,7 +141,7 @@ abstract class AbstractScriptEvaluationExtension : ScriptEvaluationExtension {
} }
} }
private fun ScriptDiagnostic.Severity.toCompilerMessageSeverity(): CompilerMessageSeverity = fun ScriptDiagnostic.Severity.toCompilerMessageSeverity(): CompilerMessageSeverity =
when (this) { when (this) {
ScriptDiagnostic.Severity.FATAL -> CompilerMessageSeverity.EXCEPTION ScriptDiagnostic.Severity.FATAL -> CompilerMessageSeverity.EXCEPTION
ScriptDiagnostic.Severity.ERROR -> CompilerMessageSeverity.ERROR ScriptDiagnostic.Severity.ERROR -> CompilerMessageSeverity.ERROR
@@ -1,5 +1,5 @@
import org.jetbrains.kotlin.scripts.* import org.jetbrains.kotlin.scripting.compiler.test.*
// this script expected parameter num : Int // this script expected parameter num : Int
@@ -5,8 +5,8 @@
package org.jetbrains.kotlin.scripting.compiler.plugin package org.jetbrains.kotlin.scripting.compiler.plugin
import junit.framework.Assert
import org.jetbrains.kotlin.cli.common.environment.setIdeaIoUseFallback import org.jetbrains.kotlin.cli.common.environment.setIdeaIoUseFallback
import org.junit.Assert
import org.junit.Test import org.junit.Test
import java.io.File import java.io.File
@@ -7,9 +7,9 @@ package org.jetbrains.kotlin.scripting.compiler.plugin
import com.intellij.openapi.Disposable import com.intellij.openapi.Disposable
import com.intellij.openapi.util.Disposer import com.intellij.openapi.util.Disposer
import junit.framework.Assert
import org.jetbrains.kotlin.cli.common.CLITool import org.jetbrains.kotlin.cli.common.CLITool
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler
import org.junit.Assert
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.io.File import java.io.File
import java.io.InputStream import java.io.InputStream
@@ -1,9 +1,9 @@
/* /*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors. * Copyright 2010-2020 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. * 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.scripts package org.jetbrains.kotlin.scripting.compiler.test
import junit.framework.TestCase import junit.framework.TestCase
import org.jetbrains.kotlin.cli.common.config.addKotlinSourceRoot import org.jetbrains.kotlin.cli.common.config.addKotlinSourceRoot
@@ -21,7 +21,7 @@ import java.io.File
import kotlin.script.experimental.host.ScriptingHostConfiguration import kotlin.script.experimental.host.ScriptingHostConfiguration
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
private const val testDataPath = "compiler/testData/script/collectDependencies" private const val testDataPath = "plugins/scripting/scripting-compiler/testData/compiler/collectDependencies"
class CollectScriptCompilationDependenciesTest : KtUsefulTestCase() { class CollectScriptCompilationDependenciesTest : KtUsefulTestCase() {
@@ -1,19 +1,17 @@
/* /*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors. * Copyright 2010-2020 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. * 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.scripts package org.jetbrains.kotlin.scripting.compiler.test
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY
import org.jetbrains.kotlin.cli.common.ExitCode import org.jetbrains.kotlin.cli.common.ExitCode
import org.jetbrains.kotlin.cli.common.config.addKotlinSourceRoot
import org.jetbrains.kotlin.cli.common.messages.MessageCollector import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler
import org.jetbrains.kotlin.daemon.TestMessageCollector
import org.jetbrains.kotlin.script.loadScriptingPlugin import org.jetbrains.kotlin.script.loadScriptingPlugin
import org.jetbrains.kotlin.scripting.compiler.plugin.TestMessageCollector
import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import org.jetbrains.kotlin.test.ConfigurationKind import org.jetbrains.kotlin.test.ConfigurationKind
@@ -26,13 +24,10 @@ import java.io.File
import kotlin.reflect.KClass import kotlin.reflect.KClass
import kotlin.script.experimental.annotations.KotlinScript import kotlin.script.experimental.annotations.KotlinScript
import kotlin.script.experimental.api.* import kotlin.script.experimental.api.*
import kotlin.script.experimental.host.FileBasedScriptSource import kotlin.script.experimental.host.*
import kotlin.script.experimental.host.FileScriptSource
import kotlin.script.experimental.host.ScriptingHostConfiguration
import kotlin.script.experimental.host.configurationDependencies
import kotlin.script.experimental.jvm.* import kotlin.script.experimental.jvm.*
private const val testDataPath = "compiler/testData/script/cliCompilation" private const val testDataPath = "plugins/scripting/scripting-compiler/testData/cliCompilation"
class ScriptCliCompilationTest : KtUsefulTestCase() { class ScriptCliCompilationTest : KtUsefulTestCase() {
@@ -69,7 +64,6 @@ class ScriptCliCompilationTest : KtUsefulTestCase() {
val configuration = KotlinTestUtils.newConfiguration(ConfigurationKind.NO_KOTLIN_REFLECT, TestJdkKind.FULL_JDK).apply { val configuration = KotlinTestUtils.newConfiguration(ConfigurationKind.NO_KOTLIN_REFLECT, TestJdkKind.FULL_JDK).apply {
put(MESSAGE_COLLECTOR_KEY, collector) put(MESSAGE_COLLECTOR_KEY, collector)
addKotlinSourceRoot(script.path)
if (scriptDef != null) { if (scriptDef != null) {
val hostConfiguration = ScriptingHostConfiguration(defaultJvmScriptingHostConfiguration) { val hostConfiguration = ScriptingHostConfiguration(defaultJvmScriptingHostConfiguration) {
configurationDependencies(JvmDependency(classpath)) configurationDependencies(JvmDependency(classpath))
@@ -84,7 +78,7 @@ class ScriptCliCompilationTest : KtUsefulTestCase() {
val environment = KotlinCoreEnvironment.createForTests(testRootDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES) val environment = KotlinCoreEnvironment.createForTests(testRootDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES)
return KotlinToJVMBytecodeCompiler.compileAndExecuteScript(environment, args) to collector return compileAndExecuteScript(script.toScriptSource(), environment, null, args) to collector
} }
private fun checkRun( private fun checkRun(
@@ -1,33 +1,21 @@
/* /*
* Copyright 2010-2017 JetBrains s.r.o. * Copyright 2010-2020 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.
* 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.scripts @file:Suppress("unused")
package org.jetbrains.kotlin.scripting.compiler.test
import com.intellij.openapi.util.Disposer import com.intellij.openapi.util.Disposer
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.config.addKotlinSourceRoot
import org.jetbrains.kotlin.cli.common.messages.* import org.jetbrains.kotlin.cli.common.messages.*
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler
import org.jetbrains.kotlin.codegen.CompilationException import org.jetbrains.kotlin.codegen.CompilationException
import org.jetbrains.kotlin.config.JVMConfigurationKeys import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.daemon.TestMessageCollector
import org.jetbrains.kotlin.daemon.assertHasMessage
import org.jetbrains.kotlin.daemon.toFile
import org.jetbrains.kotlin.script.loadScriptingPlugin import org.jetbrains.kotlin.script.loadScriptingPlugin
import org.jetbrains.kotlin.scripting.compiler.plugin.TestMessageCollector
import org.jetbrains.kotlin.scripting.compiler.plugin.assertHasMessage
import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys import org.jetbrains.kotlin.scripting.configuration.ScriptingConfigurationKeys
import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition import org.jetbrains.kotlin.scripting.definitions.KotlinScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
@@ -51,6 +39,7 @@ import kotlin.reflect.KClass
import kotlin.script.dependencies.* import kotlin.script.dependencies.*
import kotlin.script.experimental.dependencies.* import kotlin.script.experimental.dependencies.*
import kotlin.script.experimental.dependencies.DependenciesResolver.ResolveResult import kotlin.script.experimental.dependencies.DependenciesResolver.ResolveResult
import kotlin.script.experimental.host.toScriptSource
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
import kotlin.script.templates.AcceptedAnnotations import kotlin.script.templates.AcceptedAnnotations
import kotlin.script.templates.ScriptTemplateDefinition import kotlin.script.templates.ScriptTemplateDefinition
@@ -72,7 +61,8 @@ class ScriptTemplateTest : KtUsefulTestCase() {
fun testScriptWithClassParameter() { fun testScriptWithClassParameter() {
val messageCollector = TestMessageCollector() val messageCollector = TestMessageCollector()
val aClass = compileScript("fib_cp.kts", ScriptWithClassParam::class, null, runIsolated = false, messageCollector = messageCollector) val aClass =
compileScript("fib_cp.kts", ScriptWithClassParam::class, null, runIsolated = false, messageCollector = messageCollector)
Assert.assertNotNull("Compilation failed:\n$messageCollector", aClass) Assert.assertNotNull("Compilation failed:\n$messageCollector", aClass)
val out = captureOut { val out = captureOut {
aClass!!.getConstructor(TestParamClass::class.java).newInstance(TestParamClass(4)) aClass!!.getConstructor(TestParamClass::class.java).newInstance(TestParamClass(4))
@@ -82,7 +72,8 @@ class ScriptTemplateTest : KtUsefulTestCase() {
fun testScriptWithBaseClassWithParam() { fun testScriptWithBaseClassWithParam() {
val messageCollector = TestMessageCollector() val messageCollector = TestMessageCollector()
val aClass = compileScript("fib_dsl.kts", ScriptWithBaseClass::class, null, runIsolated = false, messageCollector = messageCollector) val aClass =
compileScript("fib_dsl.kts", ScriptWithBaseClass::class, null, runIsolated = false, messageCollector = messageCollector)
Assert.assertNotNull("Compilation failed:\n$messageCollector", aClass) Assert.assertNotNull("Compilation failed:\n$messageCollector", aClass)
val out = captureOut { val out = captureOut {
aClass!!.getConstructor(Integer.TYPE, Integer.TYPE).newInstance(4, 1) aClass!!.getConstructor(Integer.TYPE, Integer.TYPE).newInstance(4, 1)
@@ -106,9 +97,15 @@ class ScriptTemplateTest : KtUsefulTestCase() {
val savedErr = System.err val savedErr = System.err
try { try {
System.setErr(PrintStream(NullOutputStream())) System.setErr(PrintStream(NullOutputStream()))
Assert.assertNull(compileScript("fib_ext_ann2.kts", ScriptWithIntParamAndDummyResolver::class, null, includeKotlinRuntime = false)) Assert.assertNull(
} compileScript(
finally { "fib_ext_ann2.kts",
ScriptWithIntParamAndDummyResolver::class,
null,
includeKotlinRuntime = false
)
)
} finally {
System.setErr(savedErr) System.setErr(savedErr)
} }
@@ -133,7 +130,12 @@ class ScriptTemplateTest : KtUsefulTestCase() {
fun testScriptWithOverriddenParam() { fun testScriptWithOverriddenParam() {
val messageCollector = TestMessageCollector() val messageCollector = TestMessageCollector()
val aClass = compileScript("overridden_parameter.kts", ScriptBaseClassWithOverriddenProperty::class, null, messageCollector = messageCollector) val aClass = compileScript(
"overridden_parameter.kts",
ScriptBaseClassWithOverriddenProperty::class,
null,
messageCollector = messageCollector
)
Assert.assertNotNull("Compilation failed:\n$messageCollector", aClass) Assert.assertNotNull("Compilation failed:\n$messageCollector", aClass)
val out = captureOut { val out = captureOut {
aClass!!.getConstructor(Integer.TYPE).newInstance(4) aClass!!.getConstructor(Integer.TYPE).newInstance(4)
@@ -176,7 +178,8 @@ class ScriptTemplateTest : KtUsefulTestCase() {
fun testScriptWithNullableProjection() { fun testScriptWithNullableProjection() {
val messageCollector = TestMessageCollector() val messageCollector = TestMessageCollector()
val aClass = compileScript("nullable_projection.kts", ScriptWithNullableProjection::class, null, messageCollector = messageCollector) val aClass =
compileScript("nullable_projection.kts", ScriptWithNullableProjection::class, null, messageCollector = messageCollector)
Assert.assertNotNull("Compilation failed:\n$messageCollector", aClass) Assert.assertNotNull("Compilation failed:\n$messageCollector", aClass)
captureOut { captureOut {
aClass!!.getConstructor(Array<String>::class.java).newInstance(arrayOf<String?>(null)) aClass!!.getConstructor(Array<String>::class.java).newInstance(arrayOf<String?>(null))
@@ -203,7 +206,7 @@ class ScriptTemplateTest : KtUsefulTestCase() {
captureOut { captureOut {
aClass!!.getConstructor(Array<String>::class.java).newInstance(arrayOf("4", "other")) aClass!!.getConstructor(Array<String>::class.java).newInstance(arrayOf("4", "other"))
}.let { }.let {
assertEqualsTrimmed(NUM_4_LINE + " (other)" + FIB_SCRIPT_OUTPUT_TAIL, it) assertEqualsTrimmed("$NUM_4_LINE (other)$FIB_SCRIPT_OUTPUT_TAIL", it)
} }
} }
@@ -263,19 +266,34 @@ class ScriptTemplateTest : KtUsefulTestCase() {
fun testAcceptedAnnotationsSync() { fun testAcceptedAnnotationsSync() {
val messageCollector = TestMessageCollector() val messageCollector = TestMessageCollector()
val aClass = compileScript("acceptedAnnotations.kts", ScriptWithAcceptedAnnotationsSyncResolver::class, null, messageCollector = messageCollector) val aClass = compileScript(
"acceptedAnnotations.kts",
ScriptWithAcceptedAnnotationsSyncResolver::class,
null,
messageCollector = messageCollector
)
Assert.assertNotNull("Compilation failed:\n$messageCollector", aClass) Assert.assertNotNull("Compilation failed:\n$messageCollector", aClass)
} }
fun testAcceptedAnnotationsAsync() { fun testAcceptedAnnotationsAsync() {
val messageCollector = TestMessageCollector() val messageCollector = TestMessageCollector()
val aClass = compileScript("acceptedAnnotations.kts", ScriptWithAcceptedAnnotationsAsyncResolver::class, null, messageCollector = messageCollector) val aClass = compileScript(
"acceptedAnnotations.kts",
ScriptWithAcceptedAnnotationsAsyncResolver::class,
null,
messageCollector = messageCollector
)
Assert.assertNotNull("Compilation failed:\n$messageCollector", aClass) Assert.assertNotNull("Compilation failed:\n$messageCollector", aClass)
} }
fun testAcceptedAnnotationsLegacy() { fun testAcceptedAnnotationsLegacy() {
val messageCollector = TestMessageCollector() val messageCollector = TestMessageCollector()
val aClass = compileScript("acceptedAnnotations.kts", ScriptWithAcceptedAnnotationsLegacyResolver::class, null, messageCollector = messageCollector) val aClass = compileScript(
"acceptedAnnotations.kts",
ScriptWithAcceptedAnnotationsLegacyResolver::class,
null,
messageCollector = messageCollector
)
Assert.assertNotNull("Compilation failed:\n$messageCollector", aClass) Assert.assertNotNull("Compilation failed:\n$messageCollector", aClass)
} }
@@ -305,8 +323,7 @@ class ScriptTemplateTest : KtUsefulTestCase() {
var exceptionThrown = false var exceptionThrown = false
try { try {
tryConstructClassFromStringArgs(aClass!!, emptyList()) tryConstructClassFromStringArgs(aClass!!, emptyList())
} } catch (e: InvocationTargetException) {
catch (e: InvocationTargetException) {
Assert.assertTrue(e.cause is IllegalStateException) Assert.assertTrue(e.cause is IllegalStateException)
exceptionThrown = true exceptionThrown = true
} }
@@ -321,17 +338,19 @@ class ScriptTemplateTest : KtUsefulTestCase() {
} }
private fun compileScript( private fun compileScript(
scriptPath: String, scriptPath: String,
scriptTemplate: KClass<out Any>, scriptTemplate: KClass<out Any>,
environment: Map<String, Any?>? = null, environment: Map<String, Any?>? = null,
runIsolated: Boolean = true, runIsolated: Boolean = true,
messageCollector: MessageCollector = PrintingMessageCollector(System.err, MessageRenderer.PLAIN_FULL_PATHS, false), messageCollector: MessageCollector = PrintingMessageCollector(System.err, MessageRenderer.PLAIN_FULL_PATHS, false),
includeKotlinRuntime: Boolean = true includeKotlinRuntime: Boolean = true
): Class<*>? = ): Class<*>? =
compileScriptImpl("compiler/testData/script/" + scriptPath, compileScriptImpl(
KotlinScriptDefinitionFromAnnotatedTemplate( "plugins/scripting/scripting-compiler/testData/compiler/$scriptPath",
scriptTemplate, environment KotlinScriptDefinitionFromAnnotatedTemplate(
), runIsolated, messageCollector, includeKotlinRuntime) scriptTemplate, environment
), runIsolated, messageCollector, includeKotlinRuntime
)
private fun compileScriptImpl( private fun compileScriptImpl(
scriptPath: String, scriptPath: String,
@@ -343,13 +362,13 @@ class ScriptTemplateTest : KtUsefulTestCase() {
val rootDisposable = Disposer.newDisposable() val rootDisposable = Disposer.newDisposable()
try { try {
val additionalClasspath = System.getProperty("kotlin.test.script.classpath")?.split(File.pathSeparator) val additionalClasspath = System.getProperty("kotlin.test.script.classpath")?.split(File.pathSeparator)
?.map{ File(it) }.orEmpty().toTypedArray() ?.map { File(it) }.orEmpty().toTypedArray()
val configuration = KotlinTestUtils.newConfiguration( val configuration = KotlinTestUtils.newConfiguration(
if (includeKotlinRuntime) ConfigurationKind.ALL else ConfigurationKind.JDK_ONLY, if (includeKotlinRuntime) ConfigurationKind.ALL else ConfigurationKind.JDK_ONLY,
TestJdkKind.FULL_JDK, TestJdkKind.FULL_JDK,
*additionalClasspath) *additionalClasspath
)
configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector) configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector)
configuration.addKotlinSourceRoot(scriptPath)
configuration.add( configuration.add(
ScriptingConfigurationKeys.SCRIPT_DEFINITIONS, ScriptingConfigurationKeys.SCRIPT_DEFINITIONS,
ScriptDefinition.FromLegacy( ScriptDefinition.FromLegacy(
@@ -365,7 +384,12 @@ class ScriptTemplateTest : KtUsefulTestCase() {
val environment = KotlinCoreEnvironment.createForTests(rootDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES) val environment = KotlinCoreEnvironment.createForTests(rootDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES)
return try { return try {
KotlinToJVMBytecodeCompiler.compileScript(environment, this::class.java.classLoader.takeUnless { runIsolated }) val res = compileScript(
File(scriptPath).toScriptSource(),
environment,
this::class.java.classLoader.takeUnless { runIsolated }
)
res.first?.java
} catch (e: CompilationException) { } catch (e: CompilationException) {
messageCollector.report( messageCollector.report(
CompilerMessageSeverity.EXCEPTION, OutputMessageUtil.renderException(e), CompilerMessageSeverity.EXCEPTION, OutputMessageUtil.renderException(e),
@@ -388,21 +412,24 @@ class ScriptTemplateTest : KtUsefulTestCase() {
open class TestKotlinScriptDummyDependenciesResolver : DependenciesResolver { open class TestKotlinScriptDummyDependenciesResolver : DependenciesResolver {
@AcceptedAnnotations(DependsOn::class, DependsOnTwo::class) @AcceptedAnnotations(DependsOn::class, DependsOnTwo::class)
override fun resolve(scriptContents: ScriptContents, override fun resolve(
environment: Environment scriptContents: ScriptContents,
): ResolveResult environment: Environment
{ ): ResolveResult {
return ScriptDependencies( return ScriptDependencies(
classpath = classpathFromClassloader(), classpath = classpathFromClassloader(),
imports = listOf("org.jetbrains.kotlin.scripts.DependsOn", "org.jetbrains.kotlin.scripts.DependsOnTwo") imports = listOf(
"org.jetbrains.kotlin.scripting.compiler.test.DependsOn",
"org.jetbrains.kotlin.scripting.compiler.test.DependsOnTwo"
)
).asSuccess() ).asSuccess()
} }
} }
private fun classpathFromClassloader(): List<File> = private fun classpathFromClassloader(): List<File> =
(TestKotlinScriptDependenciesResolver::class.java.classLoader as? URLClassLoader)?.urLs (TestKotlinScriptDependenciesResolver::class.java.classLoader as? URLClassLoader)?.urLs
?.mapNotNull(URL::toFile) ?.mapNotNull(URL::toFile)
?.filter { it.path.contains("out") && it.path.contains("test") } ?.filter { it.path.contains("out") && it.path.contains("test") }
?: emptyList() ?: emptyList()
@@ -430,7 +457,10 @@ open class TestKotlinScriptDependenciesResolver : TestKotlinScriptDummyDependenc
} }
return ScriptDependencies( return ScriptDependencies(
classpath = classpathFromClassloader() + cp, classpath = classpathFromClassloader() + cp,
imports = listOf("org.jetbrains.kotlin.scripts.DependsOn", "org.jetbrains.kotlin.scripts.DependsOnTwo") imports = listOf(
"org.jetbrains.kotlin.scripting.compiler.test.DependsOn",
"org.jetbrains.kotlin.scripting.compiler.test.DependsOnTwo"
)
).asSuccess() ).asSuccess()
} }
} }
@@ -439,36 +469,38 @@ class TestParamClass(@Suppress("unused") val memberNum: Int)
class ErrorReportingResolver : TestKotlinScriptDependenciesResolver() { class ErrorReportingResolver : TestKotlinScriptDependenciesResolver() {
override fun resolve( override fun resolve(
scriptContents: ScriptContents, scriptContents: ScriptContents,
environment: Environment environment: Environment
): ResolveResult { ): ResolveResult {
return ResolveResult.Success( return ResolveResult.Success(
super.resolve(scriptContents, environment).dependencies!!, super.resolve(scriptContents, environment).dependencies!!,
listOf( listOf(
ScriptReport("error", ScriptReport.Severity.ERROR, null), ScriptReport("error", ScriptReport.Severity.ERROR, null),
ScriptReport("warning", ScriptReport.Severity.WARNING, ScriptReport.Position(1, 0)), ScriptReport("warning", ScriptReport.Severity.WARNING, ScriptReport.Position(1, 0)),
ScriptReport("info", ScriptReport.Severity.INFO, ScriptReport.Position(2, 0)), ScriptReport("info", ScriptReport.Severity.INFO, ScriptReport.Position(2, 0)),
ScriptReport("debug", ScriptReport.Severity.DEBUG, ScriptReport.Position(3, 0)) ScriptReport("debug", ScriptReport.Severity.DEBUG, ScriptReport.Position(3, 0))
) )
) )
} }
} }
class TestAsyncResolver : TestKotlinScriptDependenciesResolver(), AsyncDependenciesResolver { class TestAsyncResolver : TestKotlinScriptDependenciesResolver(), AsyncDependenciesResolver {
override suspend fun resolveAsync( override suspend fun resolveAsync(
scriptContents: ScriptContents, scriptContents: ScriptContents,
environment: Environment environment: Environment
): ResolveResult = super<TestKotlinScriptDependenciesResolver>.resolve(scriptContents, environment) ): ResolveResult = super<TestKotlinScriptDependenciesResolver>.resolve(scriptContents, environment)
override fun resolve(scriptContents: ScriptContents, environment: Environment): ResolveResult = override fun resolve(scriptContents: ScriptContents, environment: Environment): ResolveResult =
super<AsyncDependenciesResolver>.resolve(scriptContents, environment) super<AsyncDependenciesResolver>.resolve(scriptContents, environment)
} }
@Target(AnnotationTarget.FILE) @Target(AnnotationTarget.FILE)
annotation class TestAnno1 annotation class TestAnno1
@Target(AnnotationTarget.FILE) @Target(AnnotationTarget.FILE)
annotation class TestAnno2 annotation class TestAnno2
@Target(AnnotationTarget.FILE) @Target(AnnotationTarget.FILE)
annotation class TestAnno3 annotation class TestAnno3
@@ -478,38 +510,38 @@ interface AcceptedAnnotationsCheck {
fun checkHasAnno1Annotation(scriptContents: ScriptContents): ResolveResult.Success { fun checkHasAnno1Annotation(scriptContents: ScriptContents): ResolveResult.Success {
val actualAnnotations = scriptContents.annotations val actualAnnotations = scriptContents.annotations
Assert.assertTrue( Assert.assertTrue(
"Loaded annotation: $actualAnnotations", "Loaded annotation: $actualAnnotations",
actualAnnotations.single().annotationClass.qualifiedName == TestAnno1::class.qualifiedName actualAnnotations.single().annotationClass.qualifiedName == TestAnno1::class.qualifiedName
) )
return ScriptDependencies( return ScriptDependencies(
classpath = classpathFromClassloader(), classpath = classpathFromClassloader(),
imports = annotationFqNames imports = annotationFqNames
).asSuccess() ).asSuccess()
} }
} }
class TestAcceptedAnnotationsSyncResolver: DependenciesResolver, AcceptedAnnotationsCheck { class TestAcceptedAnnotationsSyncResolver : DependenciesResolver, AcceptedAnnotationsCheck {
@AcceptedAnnotations(TestAnno1::class, TestAnno3::class) @AcceptedAnnotations(TestAnno1::class, TestAnno3::class)
override fun resolve(scriptContents: ScriptContents, environment: Environment): ResolveResult { override fun resolve(scriptContents: ScriptContents, environment: Environment): ResolveResult {
return checkHasAnno1Annotation(scriptContents) return checkHasAnno1Annotation(scriptContents)
} }
} }
class TestAcceptedAnnotationsAsyncResolver: AsyncDependenciesResolver, AcceptedAnnotationsCheck { class TestAcceptedAnnotationsAsyncResolver : AsyncDependenciesResolver, AcceptedAnnotationsCheck {
@AcceptedAnnotations(TestAnno1::class, TestAnno3::class) @AcceptedAnnotations(TestAnno1::class, TestAnno3::class)
override suspend fun resolveAsync(scriptContents: ScriptContents, environment: Environment): ResolveResult { override suspend fun resolveAsync(scriptContents: ScriptContents, environment: Environment): ResolveResult {
return checkHasAnno1Annotation(scriptContents) return checkHasAnno1Annotation(scriptContents)
} }
} }
class TestAcceptedAnnotationsLegacyResolver: ScriptDependenciesResolver, AcceptedAnnotationsCheck { class TestAcceptedAnnotationsLegacyResolver : ScriptDependenciesResolver, AcceptedAnnotationsCheck {
@AcceptedAnnotations(TestAnno1::class, TestAnno3::class) @AcceptedAnnotations(TestAnno1::class, TestAnno3::class)
override fun resolve( override fun resolve(
script: ScriptContents, script: ScriptContents,
environment: Environment?, environment: Environment?,
report: (ScriptDependenciesResolver.ReportSeverity, String, ScriptContents.Position?) -> Unit, report: (ScriptDependenciesResolver.ReportSeverity, String, ScriptContents.Position?) -> Unit,
previousDependencies: KotlinScriptExternalDependencies? previousDependencies: KotlinScriptExternalDependencies?
): Future<KotlinScriptExternalDependencies?> { ): Future<KotlinScriptExternalDependencies?> {
checkHasAnno1Annotation(script) checkHasAnno1Annotation(script)
return object : KotlinScriptExternalDependencies { return object : KotlinScriptExternalDependencies {
@@ -522,51 +554,58 @@ class TestAcceptedAnnotationsLegacyResolver: ScriptDependenciesResolver, Accepte
} }
} }
class SeveralConstructorsResolver(val c: Int): TestKotlinScriptDependenciesResolver() { class SeveralConstructorsResolver(val c: Int) : TestKotlinScriptDependenciesResolver() {
constructor(): this(0) constructor() : this(0)
} }
class DefaultArgsConstructorResolver(val c: Int = 0): TestKotlinScriptDependenciesResolver()
class ThrowingResolver: DependenciesResolver { class DefaultArgsConstructorResolver(val c: Int = 0) : TestKotlinScriptDependenciesResolver()
class ThrowingResolver : DependenciesResolver {
override fun resolve(scriptContents: ScriptContents, environment: Environment): ResolveResult { override fun resolve(scriptContents: ScriptContents, environment: Environment): ResolveResult {
throw IllegalStateException("Exception from resolver") throw IllegalStateException("Exception from resolver")
} }
} }
@ScriptTemplateDefinition( @ScriptTemplateDefinition(
scriptFilePattern =".*\\.kts", scriptFilePattern = ".*\\.kts",
resolver = TestKotlinScriptDummyDependenciesResolver::class) resolver = TestKotlinScriptDummyDependenciesResolver::class
)
abstract class ScriptWithIntParamAndDummyResolver(val num: Int) abstract class ScriptWithIntParamAndDummyResolver(val num: Int)
@ScriptTemplateDefinition( @ScriptTemplateDefinition(
scriptFilePattern =".*\\.kts", scriptFilePattern = ".*\\.kts",
resolver = TestKotlinScriptDependenciesResolver::class) resolver = TestKotlinScriptDependenciesResolver::class
)
abstract class ScriptWithIntParam(val num: Int) abstract class ScriptWithIntParam(val num: Int)
@ScriptTemplateDefinition( @ScriptTemplateDefinition(
scriptFilePattern =".*\\.kts", scriptFilePattern = ".*\\.kts",
resolver = TestKotlinScriptDependenciesResolver::class) resolver = TestKotlinScriptDependenciesResolver::class
)
abstract class ScriptWithClassParam(val param: TestParamClass) abstract class ScriptWithClassParam(val param: TestParamClass)
@ScriptTemplateDefinition( @ScriptTemplateDefinition(
scriptFilePattern =".*\\.kts", scriptFilePattern = ".*\\.kts",
resolver = TestKotlinScriptDependenciesResolver::class) resolver = TestKotlinScriptDependenciesResolver::class
)
abstract class ScriptWithBaseClass(val num: Int, passthrough: Int) : TestDSLClassWithParam(passthrough) abstract class ScriptWithBaseClass(val num: Int, passthrough: Int) : TestDSLClassWithParam(passthrough)
@ScriptTemplateDefinition( @ScriptTemplateDefinition(
scriptFilePattern =".*\\.kts", scriptFilePattern = ".*\\.kts",
resolver = TestKotlinScriptDependenciesResolver::class) resolver = TestKotlinScriptDependenciesResolver::class
)
abstract class ScriptWithoutParams(@Suppress("UNUSED_PARAMETER") num: Int) abstract class ScriptWithoutParams(@Suppress("UNUSED_PARAMETER") num: Int)
@ScriptTemplateDefinition( @ScriptTemplateDefinition(
scriptFilePattern =".*\\.kts", scriptFilePattern = ".*\\.kts",
resolver = TestKotlinScriptDependenciesResolver::class) resolver = TestKotlinScriptDependenciesResolver::class
)
abstract class ScriptBaseClassWithOverriddenProperty(override val num: Int) : TestClassWithOverridableProperty(num) abstract class ScriptBaseClassWithOverriddenProperty(override val num: Int) : TestClassWithOverridableProperty(num)
@ScriptTemplateDefinition( @ScriptTemplateDefinition(
scriptFilePattern = ".*\\.custom\\.kts", scriptFilePattern = ".*\\.custom\\.kts",
resolver = TestKotlinScriptDependenciesResolver::class resolver = TestKotlinScriptDependenciesResolver::class
) )
abstract class ScriptWithDifferentFileNamePattern abstract class ScriptWithDifferentFileNamePattern
@@ -618,7 +657,15 @@ annotation class DependsOn(val path: String)
annotation class DependsOnTwo(val unused: String = "", val path1: String = "", val path2: String = "") annotation class DependsOnTwo(val unused: String = "", val path1: String = "", val path2: String = "")
private class NullOutputStream : OutputStream() { private class NullOutputStream : OutputStream() {
override fun write(b: Int) { } override fun write(b: Int) {}
override fun write(b: ByteArray) { } override fun write(b: ByteArray) {}
override fun write(b: ByteArray, off: Int, len: Int) { } override fun write(b: ByteArray, off: Int, len: Int) {}
} }
internal fun URL.toFile() =
try {
File(toURI().schemeSpecificPart)
} catch (e: java.net.URISyntaxException) {
if (protocol != "file") null
else File(file)
}
@@ -1,28 +1,15 @@
/* /*
* Copyright 2010-2017 JetBrains s.r.o. * Copyright 2010-2020 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.
* 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.scripts package org.jetbrains.kotlin.scripting.compiler.test
import com.intellij.openapi.util.Disposer import com.intellij.openapi.util.Disposer
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.config.addKotlinSourceRoot
import org.jetbrains.kotlin.cli.common.messages.* import org.jetbrains.kotlin.cli.common.messages.*
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler
import org.jetbrains.kotlin.codegen.CompilationException import org.jetbrains.kotlin.codegen.CompilationException
import org.jetbrains.kotlin.config.JVMConfigurationKeys import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.script.loadScriptingPlugin import org.jetbrains.kotlin.script.loadScriptingPlugin
@@ -38,6 +25,7 @@ import org.jetbrains.kotlin.utils.tryConstructClassFromStringArgs
import org.junit.Assert import org.junit.Assert
import java.io.File import java.io.File
import java.net.URLClassLoader import java.net.URLClassLoader
import kotlin.script.experimental.host.toScriptSource
import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration import kotlin.script.experimental.jvm.defaultJvmScriptingHostConfiguration
class ScriptTest : KtUsefulTestCase() { class ScriptTest : KtUsefulTestCase() {
@@ -48,7 +36,7 @@ class ScriptTest : KtUsefulTestCase() {
val anObj = tryConstructClassFromStringArgs(aClass!!, listOf("4", "comment")) val anObj = tryConstructClassFromStringArgs(aClass!!, listOf("4", "comment"))
Assert.assertNotNull(anObj) Assert.assertNotNull(anObj)
} }
assertEqualsTrimmed(NUM_4_LINE + " (comment)" + FIB_SCRIPT_OUTPUT_TAIL, out) assertEqualsTrimmed("$NUM_4_LINE (comment)$FIB_SCRIPT_OUTPUT_TAIL", out)
} }
fun testStandardScriptWithoutParams() { fun testStandardScriptWithoutParams() {
@@ -58,7 +46,7 @@ class ScriptTest : KtUsefulTestCase() {
val anObj = tryConstructClassFromStringArgs(aClass!!, emptyList()) val anObj = tryConstructClassFromStringArgs(aClass!!, emptyList())
Assert.assertNotNull(anObj) Assert.assertNotNull(anObj)
} }
assertEqualsTrimmed(NUM_4_LINE + " (none)" + FIB_SCRIPT_OUTPUT_TAIL, out) assertEqualsTrimmed("$NUM_4_LINE (none)$FIB_SCRIPT_OUTPUT_TAIL", out)
} }
fun testStandardScriptWithSaving() { fun testStandardScriptWithSaving() {
@@ -70,7 +58,7 @@ class ScriptTest : KtUsefulTestCase() {
val anObj = tryConstructClassFromStringArgs(aClass!!, emptyList()) val anObj = tryConstructClassFromStringArgs(aClass!!, emptyList())
Assert.assertNotNull(anObj) Assert.assertNotNull(anObj)
} }
assertEqualsTrimmed(NUM_4_LINE + " (none)" + FIB_SCRIPT_OUTPUT_TAIL, out1) assertEqualsTrimmed("$NUM_4_LINE (none)$FIB_SCRIPT_OUTPUT_TAIL", out1)
val savedClassLoader = URLClassLoader(arrayOf(tmpdir.toURI().toURL()), aClass!!.classLoader) val savedClassLoader = URLClassLoader(arrayOf(tmpdir.toURI().toURL()), aClass!!.classLoader)
val aClassSaved = savedClassLoader.loadClass(aClass.name) val aClassSaved = savedClassLoader.loadClass(aClass.name)
Assert.assertNotNull(aClassSaved) Assert.assertNotNull(aClassSaved)
@@ -78,7 +66,7 @@ class ScriptTest : KtUsefulTestCase() {
val anObjSaved = tryConstructClassFromStringArgs(aClassSaved!!, emptyList()) val anObjSaved = tryConstructClassFromStringArgs(aClassSaved!!, emptyList())
Assert.assertNotNull(anObjSaved) Assert.assertNotNull(anObjSaved)
} }
assertEqualsTrimmed(NUM_4_LINE + " (none)" + FIB_SCRIPT_OUTPUT_TAIL, out2) assertEqualsTrimmed("$NUM_4_LINE (none)$FIB_SCRIPT_OUTPUT_TAIL", out2)
} }
fun testUseCompilerInternals() { fun testUseCompilerInternals() {
@@ -96,14 +84,13 @@ class ScriptTest : KtUsefulTestCase() {
saveClassesDir: File? = null saveClassesDir: File? = null
): Class<*>? { ): Class<*>? {
val messageCollector = val messageCollector =
if (suppressOutput) MessageCollector.NONE if (suppressOutput) MessageCollector.NONE
else PrintingMessageCollector(System.err, MessageRenderer.PLAIN_FULL_PATHS, false) else PrintingMessageCollector(System.err, MessageRenderer.PLAIN_FULL_PATHS, false)
val rootDisposable = Disposer.newDisposable() val rootDisposable = Disposer.newDisposable()
try { try {
val configuration = KotlinTestUtils.newConfiguration(ConfigurationKind.ALL, TestJdkKind.FULL_JDK) val configuration = KotlinTestUtils.newConfiguration(ConfigurationKind.ALL, TestJdkKind.FULL_JDK)
configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector) configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector)
configuration.addKotlinSourceRoot("compiler/testData/script/$scriptPath")
configuration.add( configuration.add(
ScriptingConfigurationKeys.SCRIPT_DEFINITIONS, ScriptingConfigurationKeys.SCRIPT_DEFINITIONS,
ScriptDefinition.FromLegacy( ScriptDefinition.FromLegacy(
@@ -121,19 +108,22 @@ class ScriptTest : KtUsefulTestCase() {
val environment = KotlinCoreEnvironment.createForTests(rootDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES) val environment = KotlinCoreEnvironment.createForTests(rootDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES)
try { try {
return KotlinToJVMBytecodeCompiler.compileScript(environment, this::class.java.classLoader.takeUnless { runIsolated }) return compileScript(
} File("plugins/scripting/scripting-compiler/testData/compiler/$scriptPath").toScriptSource(),
catch (e: CompilationException) { environment,
messageCollector.report(CompilerMessageSeverity.EXCEPTION, OutputMessageUtil.renderException(e), this::class.java.classLoader.takeUnless { runIsolated }
MessageUtil.psiElementToMessageLocation(e.element)) ).first?.java
} catch (e: CompilationException) {
messageCollector.report(
CompilerMessageSeverity.EXCEPTION, OutputMessageUtil.renderException(e),
MessageUtil.psiElementToMessageLocation(e.element)
)
return null return null
} } catch (t: Throwable) {
catch (t: Throwable) {
MessageCollectorUtil.reportException(messageCollector, t) MessageCollectorUtil.reportException(messageCollector, t)
throw t throw t
} }
} } finally {
finally {
Disposer.dispose(rootDisposable) Disposer.dispose(rootDisposable)
} }
} }
@@ -16,7 +16,7 @@
@file:Suppress("unused") @file:Suppress("unused")
package org.jetbrains.kotlin.scripts package org.jetbrains.kotlin.scripting.compiler.test
open class TestDSLClass open class TestDSLClass
@@ -0,0 +1,106 @@
/*
* Copyright 2010-2020 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.compiler.test
import kotlinx.coroutines.runBlocking
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.ExitCode
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.scripting.compiler.plugin.impl.ScriptJvmCompilerFromEnvironment
import org.jetbrains.kotlin.scripting.compiler.plugin.toCompilerMessageSeverity
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionProvider
import org.jetbrains.kotlin.utils.tryConstructClassFromStringArgs
import org.junit.Assert
import java.io.ByteArrayOutputStream
import java.io.PrintStream
import kotlin.reflect.KClass
import kotlin.script.experimental.api.SourceCode
import kotlin.script.experimental.api.onSuccess
import kotlin.script.experimental.api.valueOr
import kotlin.script.experimental.api.with
import kotlin.script.experimental.jvm.baseClassLoader
import kotlin.script.experimental.jvm.dependenciesFromCurrentContext
import kotlin.script.experimental.jvm.jvm
internal const val NUM_4_LINE = "num: 4"
internal const val FIB_SCRIPT_OUTPUT_TAIL =
"""
fib(1)=1
fib(0)=1
fib(2)=2
fib(1)=1
fib(3)=3
fib(1)=1
fib(0)=1
fib(2)=2
fib(4)=5
"""
internal fun captureOut(body: () -> Unit): String {
val outStream = ByteArrayOutputStream()
val prevOut = System.out
System.setOut(PrintStream(outStream))
try {
body()
} finally {
System.out.flush()
System.setOut(prevOut)
}
return outStream.toString()
}
private fun String.linesSplitTrim() =
split('\n', '\r').map(String::trim).filter(String::isNotBlank)
internal fun assertEqualsTrimmed(expected: String, actual: String) =
Assert.assertEquals(expected.linesSplitTrim(), actual.linesSplitTrim())
// TODO: rewrite tests to avoid emulated old behavior
internal fun compileScript(
script: SourceCode,
environment: KotlinCoreEnvironment,
parentClassLoader: ClassLoader?
): Pair<KClass<*>?, ExitCode> {
val scriptCompiler = ScriptJvmCompilerFromEnvironment(environment)
val messageCollector = environment.configuration[CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY]!!
val scriptDefinition = ScriptDefinitionProvider.getInstance(environment.project)!!.findDefinition(script)!!
val scriptCompilationConfiguration = scriptDefinition.compilationConfiguration.with {
jvm {
dependenciesFromCurrentContext(wholeClasspath = true)
}
}
val compiledScript = scriptCompiler.compile(script, scriptCompilationConfiguration).onSuccess {
runBlocking {
it.getClass(scriptDefinition.evaluationConfiguration.with {
jvm {
baseClassLoader(parentClassLoader)
}
})
}
}.valueOr {
for (report in it.reports) {
messageCollector.report(report.severity.toCompilerMessageSeverity(), report.render(withSeverity = false))
}
return null to ExitCode.COMPILATION_ERROR
}
return compiledScript to ExitCode.OK
}
// TODO: rewrite tests to avoid emulated old behavior
internal fun compileAndExecuteScript(
script: SourceCode,
environment: KotlinCoreEnvironment,
parentClassLoader: ClassLoader?,
scriptArgs: List<String>
): ExitCode {
val (compiled, code) = compileScript(script, environment, parentClassLoader)
if (compiled == null || code != ExitCode.OK) return code
return if (tryConstructClassFromStringArgs(compiled.java, scriptArgs) != null) ExitCode.OK else ExitCode.INTERNAL_ERROR
}