Minimize bunch files size for AbstractScriptConfigurationTest.kt

This commit is contained in:
Natalia Selezneva
2019-10-29 11:46:15 +03:00
parent d2b895d8c2
commit 4613bf4b06
4 changed files with 47 additions and 313 deletions
@@ -306,9 +306,9 @@ abstract class AbstractScriptConfigurationTest : KotlinCompletionTestCase() {
CustomScriptTemplateProvider(environment)
}
PlatformTestUtil.registerExtension(
Extensions.getArea(project),
addExtensionPointInTest(
ScriptDefinitionContributor.EP_NAME,
project,
provider,
testRootDisposable
)
@@ -1,311 +0,0 @@
/*
* 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.idea.script
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.extensions.Extensions
import com.intellij.openapi.module.JavaModuleType
import com.intellij.openapi.module.Module
import com.intellij.openapi.projectRoots.ProjectJdkTable
import com.intellij.openapi.roots.ModuleRootModificationUtil
import com.intellij.openapi.roots.ProjectRootManager
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.util.text.StringUtil
import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.openapi.vfs.VfsUtil
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.testFramework.PlatformTestCase
import com.intellij.testFramework.PlatformTestUtil
import com.intellij.testFramework.PsiTestUtil
import com.intellij.util.ui.UIUtil
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime
import org.jetbrains.kotlin.idea.completion.test.KotlinCompletionTestCase
import org.jetbrains.kotlin.idea.core.script.IdeScriptReportSink
import org.jetbrains.kotlin.idea.core.script.ScriptConfigurationManager.Companion.updateScriptDependenciesSynchronously
import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionContributor
import org.jetbrains.kotlin.idea.core.script.ScriptDefinitionsManager
import org.jetbrains.kotlin.idea.core.script.isScriptDependenciesUpdaterDisabled
import org.jetbrains.kotlin.idea.highlighter.KotlinHighlightingUtil
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.scripting.definitions.findScriptDefinition
import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.test.MockLibraryUtil
import org.jetbrains.kotlin.test.TestJdkKind
import org.jetbrains.kotlin.test.util.addDependency
import org.jetbrains.kotlin.test.util.projectLibrary
import org.jetbrains.kotlin.utils.PathUtil
import org.jetbrains.kotlin.utils.PathUtil.KOTLIN_JAVA_SCRIPT_RUNTIME_JAR
import org.jetbrains.kotlin.utils.PathUtil.KOTLIN_SCRIPTING_COMMON_JAR
import org.jetbrains.kotlin.utils.PathUtil.KOTLIN_SCRIPTING_JVM_JAR
import java.io.File
import java.util.regex.Pattern
import kotlin.script.dependencies.Environment
import kotlin.script.experimental.api.ScriptDiagnostic
private val validKeys = setOf("javaHome", "sources", "classpath", "imports", "template-classes-names")
private const val useDefaultTemplate = "// DEPENDENCIES:"
private const val templatesSettings = "// TEMPLATES: "
// some bugs can only be reproduced when some module and script have intersecting library dependencies
private const val configureConflictingModule = "// CONFLICTING_MODULE"
private fun String.splitOrEmpty(delimeters: String) = split(delimeters).takeIf { it.size > 1 } ?: emptyList()
internal val switches = listOf(
useDefaultTemplate,
configureConflictingModule
)
abstract class AbstractScriptConfigurationTest : KotlinCompletionTestCase() {
companion object {
private const val SCRIPT_NAME = "script.kts"
}
override fun setUpModule() {
// do not create default module
}
private fun findMainScript(testDir: String): File {
val scriptFile = File(testDir).walkTopDown().find { it.name == SCRIPT_NAME }
if (scriptFile != null) return scriptFile
return File(testDir).walkTopDown().singleOrNull { it.name.contains("script") }
?: error("Couldn't find $SCRIPT_NAME file in $testDir")
}
private val sdk by lazy {
val jdk = PluginTestCaseBase.jdk(TestJdkKind.MOCK_JDK)
runWriteAction {
ProjectJdkTable.getInstance().addJdk(jdk, testRootDisposable)
ProjectRootManager.getInstance(project).projectSdk = jdk
}
jdk
}
protected fun configureScriptFile(path: String) {
val mainScriptFile = findMainScript(path)
val environment = createScriptEnvironment(mainScriptFile)
registerScriptTemplateProvider(environment)
File(path, "mainModule").takeIf { it.exists() }?.let {
myModule = createTestModuleFromDir(it)
}
File(path).listFiles { file -> file.name.startsWith("module") }.filter { it.exists() }.forEach {
val newModule = createTestModuleFromDir(it)
assert(myModule != null) { "Main module should exists" }
ModuleRootModificationUtil.addDependency(myModule, newModule)
}
if (module != null) {
module.addDependency(
projectLibrary(
"script-runtime",
classesRoot = VfsUtil.findFileByIoFile(PathUtil.kotlinPathsForDistDirectory.scriptRuntimePath, true)
)
)
if (environment["template-classes"] != null) {
module.addDependency(
projectLibrary(
"script-template-library",
classesRoot = VfsUtil.findFileByIoFile(environment["template-classes"] as File, true)
)
)
}
}
if (configureConflictingModule in environment) {
val sharedLib = VfsUtil.findFileByIoFile(environment["lib-classes"] as File, true)!!
if (module == null) {
// Force create module if it doesn't exist
myModule = createTestModuleByName("mainModule")
}
module.addDependency(projectLibrary("sharedLib", classesRoot = sharedLib))
}
if (module != null) {
ModuleRootModificationUtil.updateModel(module) { model ->
model.sdk = sdk
}
}
createFileAndSyncDependencies(mainScriptFile)
}
private val oldScripClasspath: String? = System.getProperty("kotlin.script.classpath")
override fun setUp() {
super.setUp()
ApplicationManager.getApplication().isScriptDependenciesUpdaterDisabled = true
}
override fun tearDown() {
ApplicationManager.getApplication().isScriptDependenciesUpdaterDisabled = false
System.setProperty("kotlin.script.classpath", oldScripClasspath ?: "")
super.tearDown()
}
private fun createTestModuleByName(name: String): Module {
val newModuleDir = runWriteAction { VfsUtil.createDirectoryIfMissing(project.baseDir, name) }
val newModule = createModuleAt(name, project, JavaModuleType.getModuleType(), newModuleDir.path)
// Return type was changed, but it's not used. BUNCH: 183
@Suppress("MissingRecentApi")
PsiTestUtil.addSourceContentToRoots(newModule, newModuleDir)
return newModule
}
private fun createTestModuleFromDir(dir: File): Module {
return createTestModuleByName(dir.name).apply {
PlatformTestCase.copyDirContentsTo(LocalFileSystem.getInstance().findFileByIoFile(dir)!!, moduleFile!!.parent)
}
}
private fun createScriptEnvironment(scriptFile: File): Environment {
val defaultEnvironment = defaultEnvironment(scriptFile.parent + File.separator)
val env = mutableMapOf<String, Any?>()
scriptFile.forEachLine { line ->
fun iterateKeysInLine(prefix: String) {
if (line.contains(prefix)) {
line.trim().substringAfter(prefix).split(";").forEach { entry ->
val (key, values) = entry.splitOrEmpty(":").map { it.trim() }
assert(key in validKeys) { "Unexpected key: $key" }
env[key] = values.split(",").map {
val str = it.trim()
defaultEnvironment[str] ?: str
}
}
}
}
iterateKeysInLine(useDefaultTemplate)
iterateKeysInLine(templatesSettings)
switches.forEach {
if (it in line) {
env[it] = true
}
}
}
if (env[useDefaultTemplate] != true && env["template-classes-names"] == null) {
env["template-classes-names"] = listOf("custom.scriptDefinition.Template")
}
if (env["javaHome"] != null) {
val jdkKind = when ((env["javaHome"] as? List<String>)?.singleOrNull()) {
"9" -> TestJdkKind.FULL_JDK_9
else -> TestJdkKind.MOCK_JDK
}
runWriteAction {
val jdk = PluginTestCaseBase.jdk(jdkKind)
ProjectJdkTable.getInstance().addJdk(jdk, testRootDisposable)
env["javaHome"] = File(jdk.homePath)
}
}
env.putAll(defaultEnvironment)
return env
}
private fun defaultEnvironment(path: String): Map<String, File?> {
val templateOutDir = File("${path}template").takeIf { it.isDirectory }?.let {
compileLibToDir(it, *scriptClasspath())
} ?: File("idea/testData/script/definition/defaultTemplate").takeIf { it.isDirectory }?.let {
compileLibToDir(it, *scriptClasspath())
}
val libSrcDir = File("${path}lib").takeIf { it.isDirectory }
val libClasses = libSrcDir?.let { compileLibToDir(it) }
return mapOf(
"runtime-classes" to ForTestCompileRuntime.runtimeJarForTests(),
"runtime-source" to File("libraries/stdlib/src"),
"lib-classes" to libClasses,
"lib-source" to libSrcDir,
"template-classes" to templateOutDir
)
}
private fun scriptClasspath(): Array<String> {
return with(PathUtil.kotlinPathsForDistDirectory) {
arrayOf(
File(libPath, KOTLIN_JAVA_SCRIPT_RUNTIME_JAR).path,
File(libPath, KOTLIN_SCRIPTING_COMMON_JAR).path,
File(libPath, KOTLIN_SCRIPTING_JVM_JAR).path
)
}
}
private fun createFileAndSyncDependencies(scriptFile: File) {
var script: VirtualFile? = null
if (module != null) {
script = module.moduleFile?.parent?.findChild(scriptFile.name)
}
if (script == null) {
val target = File(project.basePath, scriptFile.name)
scriptFile.copyTo(target)
script = VfsUtil.findFileByIoFile(target, true)
}
if (script == null) error("Test file with script couldn't be found in test project")
configureByExistingFile(script)
updateScriptDependenciesSynchronously(myFile, project)
VfsUtil.markDirtyAndRefresh(false, true, true, project.baseDir)
// This is needed because updateScriptDependencies invalidates psiFile that was stored in myFile field
myFile = psiManager.findFile(script)
val ktFile = myFile as KtFile
val reports = IdeScriptReportSink.getReports(ktFile)
val isFatalErrorPresent = reports.any { it.severity == ScriptDiagnostic.Severity.FATAL }
assert(isFatalErrorPresent || KotlinHighlightingUtil.shouldHighlight(myFile)) {
"Highlighting is switched off for ${myFile.virtualFile.path}\n" +
"reports=$reports\n" +
"scriptDefinition=${ktFile.findScriptDefinition()}"
}
}
private fun compileLibToDir(srcDir: File, vararg classpath: String): File {
//TODO: tmpDir would be enough, but there is tricky fail under AS otherwise
val outDir = KotlinTestUtils.tmpDirForReusableFolder("${getTestName(false)}${srcDir.name}Out")
val kotlinSourceFiles = FileUtil.findFilesByMask(Pattern.compile(".+\\.kt$"), srcDir)
if (kotlinSourceFiles.isNotEmpty()) {
MockLibraryUtil.compileKotlin(srcDir.path, outDir, extraClasspath = *classpath)
}
val javaSourceFiles = FileUtil.findFilesByMask(Pattern.compile(".+\\.java$"), srcDir)
if (javaSourceFiles.isNotEmpty()) {
KotlinTestUtils.compileJavaFiles(
javaSourceFiles,
listOf("-cp", StringUtil.join(listOf(*classpath, outDir), File.pathSeparator), "-d", outDir.path)
)
}
return outDir
}
private fun registerScriptTemplateProvider(environment: Environment) {
val provider = if (environment[useDefaultTemplate] == true) {
FromTextTemplateProvider(environment)
} else {
CustomScriptTemplateProvider(environment)
}
ScriptDefinitionContributor.EP_NAME.getPoint(null).registerExtension(provider)
ScriptDefinitionsManager.getInstance(project).reloadScriptDefinitions()
UIUtil.dispatchAllInvocationEvents()
}
}
@@ -0,0 +1,26 @@
/*
* 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.idea.script
import com.intellij.openapi.Disposable
import com.intellij.openapi.extensions.ExtensionPointName
import com.intellij.openapi.extensions.Extensions
import com.intellij.openapi.project.Project
import com.intellij.testFramework.PlatformTestUtil
fun <T> addExtensionPointInTest(
pointName: ExtensionPointName<T>,
project: Project,
provider: T,
testRootDisposable: Disposable
) {
PlatformTestUtil.registerExtension(
Extensions.getArea(project),
pointName,
provider,
testRootDisposable
)
}
@@ -0,0 +1,19 @@
/*
* 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.idea.script
import com.intellij.openapi.Disposable
import com.intellij.openapi.extensions.ExtensionPointName
import com.intellij.openapi.project.Project
fun <T> addExtensionPointInTest(
pointName: ExtensionPointName<T>,
project: Project,
provider: T,
testRootDisposable: Disposable
) {
pointName.getPoint(null).registerExtension(provider)
}