[gui-tests] Greenify compiler

This commit is contained in:
Maxim.Kolmakov
2017-10-09 13:04:17 +02:00
parent c804ff3e04
commit 9002ee0ba0
3 changed files with 62 additions and 154 deletions
@@ -15,10 +15,8 @@
*/
package com.intellij.testGuiFramework.recorder.compile
import com.intellij.ide.plugins.PluginManager
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.diagnostic.Logger
import com.intellij.testGuiFramework.recorder.actions.PerformScriptAction
import java.io.File
import java.net.URL
@@ -26,31 +24,21 @@ import java.nio.file.Paths
object KotlinCompileUtil {
private val LOG by lazy { Logger.getInstance("#${KotlinCompileUtil::class.qualifiedName}") }
private val localCompiler: LocalCompiler by lazy { LocalCompiler() }
fun compile(codeString: String) {
localCompiler.compileOnPooledThread(ScriptWrapper.wrapScript(codeString), getAllUrls().map { Paths.get(it.toURI()).toFile().path })
}
fun compileAndRun(codeString: String) {
localCompiler.compileAndRunOnPooledThread(ScriptWrapper.wrapScript(codeString),
getAllUrls().map { Paths.get(it.toURI()).toFile().path })
}
fun getAllUrls(): List<URL> {
private fun getAllUrls(): List<URL> {
if (ServiceManager::class.java.classLoader.javaClass.name.contains("Launcher\$AppClassLoader")) {
//lets substitute jars with a common lib dir to avoid Windows long path error
val urls = ServiceManager::class.java.classLoader.forcedUrls()
val libUrl = urls.filter { url ->
val libUrl = urls.first { url ->
(url.file.endsWith("idea.jar") && File(url.path).parentFile.name == "lib")
}.firstOrNull()!!.getParentURL()
}.getParentURL()
urls.filter { url -> !url.file.startsWith(libUrl.file) }.plus(libUrl).toSet()
//add git4idea urls to allow git configuration from local runner
// if (System.getenv("TEAMCITY_VERSION") != null) urls.plus(getGit4IdeaUrls())
if (!ApplicationManager.getApplication().isUnitTestMode)
urls.plus(ServiceManager::class.java.classLoader.forcedBaseUrls())
return urls.toList()
@@ -63,27 +51,13 @@ object KotlinCompileUtil {
return list
}
fun getGit4IdeaUrls(): List<URL> {
val git4IdeaPluginClassLoader = PluginManager.getPlugins().filter { pluginDescriptor -> pluginDescriptor.name.toLowerCase() == "git integration" }.firstOrNull()!!.pluginClassLoader
val urls = git4IdeaPluginClassLoader.forcedUrls()
val libUrl = urls.filter { url ->
(url.file.endsWith("git4idea.jar") && File(url.path).parentFile.name == "lib")
}.firstOrNull()!!.getParentURL()
urls.filter { url -> !url.file.startsWith(libUrl.file) }.plus(libUrl).toSet()
return urls
}
private fun URL.getParentURL() = File(this.file).parentFile.toURI().toURL()!!
private fun ClassLoader.forcedUrls(): List<URL> {
var methodName = "getUrls"
val methodAlternativeName = "getURLs"
fun URL.getParentURL() = File(this.file).parentFile.toURI().toURL()
fun ClassLoader.forcedUrls(): List<URL> {
val METHOD_DEFAULT_NAME: String = "getUrls"
val METHOD_ALTERNATIVE_NAME: String = "getURLs"
var methodName: String = METHOD_DEFAULT_NAME
if (this.javaClass.methods.any { mtd -> mtd.name == METHOD_ALTERNATIVE_NAME }) methodName = METHOD_ALTERNATIVE_NAME
if (this.javaClass.methods.any { mtd -> mtd.name == methodAlternativeName }) methodName = methodAlternativeName
val method = this.javaClass.getMethod(methodName)
method.isAccessible
val methodResult = method.invoke(this)
@@ -91,17 +65,15 @@ object KotlinCompileUtil {
return myList.filterIsInstance(URL::class.java)
}
fun ClassLoader.forcedBaseUrls(): List<URL> {
private fun ClassLoader.forcedBaseUrls(): List<URL> {
try {
return ((this.javaClass.getMethod("getBaseUrls").invoke(
this) as? List<*>)!!.filter { it is URL && it.protocol == "file" && !it.file.endsWith("jar!") }) as List<URL>
return ((this.javaClass.getMethod("getBaseUrls").invoke(this) as? List<*>)!!.
filter { it is URL && it.protocol == "file" && !it.file.endsWith("jar!") }) as List<URL>
}
catch (e: NoSuchMethodException) {
return emptyList()
}
}
}
@@ -25,13 +25,11 @@ import com.intellij.openapi.util.io.FileUtil
import com.intellij.testGuiFramework.recorder.components.GuiRecorderComponent
import com.intellij.testGuiFramework.recorder.ui.Notifier
import com.intellij.util.download.DownloadableFileService
import com.intellij.util.lang.UrlClassLoader
import java.io.BufferedReader
import java.io.File
import java.io.FileNotFoundException
import java.io.InputStreamReader
import java.util.*
import java.util.concurrent.Callable
import java.util.concurrent.TimeUnit
import java.util.stream.Collectors
@@ -42,19 +40,17 @@ class LocalCompiler {
private val LOG by lazy { Logger.getInstance("#${LocalCompiler::class.qualifiedName}") }
var codeHash: Int? = null
private var codeHash: Int? = null
val TEST_CLASS_NAME = "CurrentTest"
private val TEST_CLASS_NAME = "CurrentTest"
val helloKtText = "fun main(args: Array<String>) { \n println(\"Hello, World!\") \n }"
val kotlinCompilerJarUrl = "http://central.maven.org/maven2/org/jetbrains/kotlin/kotlin-compiler/1.0.6/kotlin-compiler-1.0.6.jar"
val kotlinCompilerJarName = "kotlin-compiler-1.0.6.jar"
private val helloKtText = "fun main(args: Array<String>) { \n println(\"Hello, World!\") \n }"
private val kotlinCompilerJarUrl = "http://central.maven.org/maven2/org/jetbrains/kotlin/kotlin-compiler/1.0.6/kotlin-compiler-1.0.6.jar"
private val kotlinCompilerJarName = "kotlin-compiler-1.0.6.jar"
val KOTLINC_PLUGIN_DIR = "kotlinc"
val tempDir by lazy { FileUtil.createTempDirectory("kotlin-compiler-tmp", null, true) }
val helloKt by lazy { createTempFile(helloKtText) }
var tempFile: File? = null
private val tempDir by lazy { FileUtil.createTempDirectory("kotlin-compiler-tmp", null, true) }
private val helloKt by lazy { createTempFile(helloKtText) }
private var tempFile: File? = null
private fun createTempFile(content: String, fileName: String = TEST_CLASS_NAME, extension: String = ".kt"): File {
val tempFile = FileUtil.createTempFile(fileName, extension, true)
@@ -64,25 +60,25 @@ class LocalCompiler {
}
fun compileAndRunOnPooledThread(code: String, classpath: List<String>) {
val taskFuture = ApplicationManager.getApplication().executeOnPooledThread({
try {
if (codeHash == null || codeHash != code.hashCode()) {
compile(code, classpath)
codeHash = code.hashCode()
}
run2()
}
catch (ce: CompilationException) {
LOG.error(ce.message)
}
})
val taskFuture = ApplicationManager.getApplication().executeOnPooledThread(
{
try {
if (codeHash == null || codeHash != code.hashCode()) {
compile(code, classpath)
codeHash = code.hashCode()
}
run()
}
catch (ce: CompilationException) {
LOG.error(ce.message)
}
})
GuiRecorderComponent.currentTask = taskFuture
}
//alternative way to run compiled code with pluginClassloader built especially for this file
private fun run2() {
private fun run() {
Notifier.updateStatus("${Notifier.LONG_OPERATION_PREFIX}Script running...")
val testUrl = tempDir.toURI().toURL()
var classLoadersArray: Array<ClassLoader>
@@ -113,52 +109,9 @@ class LocalCompiler {
}
private fun run(classpath: List<String>) {
//TODO: add case for Gradle test
if (tempFile == null) throw Exception("Unable to find tempFile")
// findClass
if (!tempDir.listFiles().any { file -> (file.name.contains(TEST_CLASS_NAME) && file.extension == "class") }) throw Exception(
"Unable to locate compiled class files")
// val pluginClassLoader = this.javaClass.classLoader as PluginClassLoader
// pluginClassLoader.addLibDirectories(listOf(tempDir.path))
//create a copy of a plugin classloader
val urlClassLoader = UrlClassLoader.build().parent(ApplicationManager::class.java.classLoader).urls(
classpath.map { it -> File(it).toURI().toURL() }.plus(tempDir.toURI().toURL())).get()
val currentTest = urlClassLoader.loadClass(TEST_CLASS_NAME) ?: throw Exception(
"Unable to load by pluginClassLoader $TEST_CLASS_NAME.class file")
val testCase = currentTest.newInstance()
val setUpMethod = currentTest.getMethod("setUp")
val testMethod = currentTest.getMethod(ScriptWrapper.TEST_METHOD_NAME)
Notifier.updateStatus("${Notifier.LONG_OPERATION_PREFIX}Script running...")
GuiRecorderComponent.state = GuiRecorderComponent.States.RUNNING
try {
setUpMethod.invoke(testCase)
testMethod.invoke(testCase)
Notifier.updateStatus("Script stopped")
GuiRecorderComponent.state = GuiRecorderComponent.States.IDLE
}
catch (throwable: Throwable) {
GuiRecorderComponent.state = GuiRecorderComponent.States.RUNNING_ERROR
Notifier.updateStatus("Running error, please see idea.log")
throw throwable
}
}
fun compileOnPooledThread(code: String, classpath: List<String>) {
ApplicationManager.getApplication().executeOnPooledThread(Callable<Boolean> {
compile(code, classpath)
})
}
fun compile(code: String, classpath: List<String>): Boolean {
val tempFile = createTempFile(code)
return compile(tempFile, classpath)
}
private fun compile(code: String, classpath: List<String>): Boolean = compile(createTempFile(code), classpath)
private fun compile(fileKt: File? = null, classpath: List<String>): Boolean {
val scriptKt = fileKt ?: helloKt
val kotlinCompilerJar = getKotlinCompilerJar()
val libDirLocation = getApplicationLibDir().parentFile
@@ -183,7 +136,7 @@ class LocalCompiler {
LOG.error(BufferedReader(InputStreamReader(process.errorStream)).lines().collect(Collectors.joining("\n")))
Notifier.updateStatus("Compilation error (see idea.log)")
GuiRecorderComponent.state = GuiRecorderComponent.States.COMPILATION_ERROR
throw CompilationException("Compilation error (see idea.log)")
throw CompilationException()
}
else {
Notifier.updateStatus("Compilation is done")
@@ -195,10 +148,8 @@ class LocalCompiler {
private fun getKotlinCompilerJar(): File {
val kotlinCompilerDir = getPluginKotlincDir()
if (!isKotlinCompilerDir(kotlinCompilerDir)) downloadKotlinCompilerJar(kotlinCompilerDir.path)
val kotlinCompilerJar = kotlinCompilerDir.listFiles().filter { file -> file.name.contains("kotlin-compiler") }.firstOrNull()
?: throw FileNotFoundException("Unable to find kotlin-compiler*.jar in ${kotlinCompilerDir.path} directory")
return kotlinCompilerJar
return kotlinCompilerDir.listFiles().firstOrNull { file -> file.name.contains("kotlin-compiler") }
?: throw FileNotFoundException("Unable to find kotlin-compiler*.jar in ${kotlinCompilerDir.path} directory")
}
private fun getApplicationLibDir(): File {
@@ -206,66 +157,47 @@ class LocalCompiler {
}
private fun getPluginKotlincDir(): File {
// if (ApplicationManager.getApplication().isUnitTestMode || (this.javaClass.classLoader.javaClass.name == "sun.misc.Launcher\$AppClassLoader")) {
val tempPath = PathManager.getTempPath()
val tempDirFile = File(tempPath)
val tempDirFile = File(PathManager.getTempPath())
FileUtil.ensureExists(tempDirFile)
return tempDirFile
// }
//
// val pluginId = (this.javaClass.classLoader as PluginClassLoader).pluginId
// val pluginPath = PluginManager.getPlugin(pluginId)!!.path
// val kotlinCompilerDirPath = pluginPath.path + File.separator + KOTLINC_PLUGIN_DIR
// val kotlinCompilerDir = File(kotlinCompilerDirPath)
//
//// ensure that dir for kotlinc is created
// FileUtil.ensureExists(kotlinCompilerDir)
// return kotlinCompilerDir
}
private fun isKotlinCompilerDir(dir: File): Boolean {
return dir.listFiles().any { file -> file.name.contains("kotlin-compiler") }
private fun isKotlinCompilerDir(dir: File): Boolean = dir.listFiles().any { file ->
file.name.contains("kotlin-compiler")
}
fun downloadKotlinCompilerJar(destDirPath: String?): File {
private fun downloadKotlinCompilerJar(destDirPath: String?): File {
Notifier.updateStatus("${Notifier.LONG_OPERATION_PREFIX}Downloading kotlin-compiler.jar...")
val downloader = DownloadableFileService.getInstance()
val description = downloader.createFileDescription(kotlinCompilerJarUrl, kotlinCompilerJarName)
ApplicationManager.getApplication().invokeAndWait({
downloader.createDownloader(Arrays.asList(description), kotlinCompilerJarName)
.downloadFilesWithProgress(destDirPath, null, null)
})
ApplicationManager.getApplication().invokeAndWait(
{
downloader.createDownloader(Arrays.asList(description), kotlinCompilerJarName).downloadFilesWithProgress(destDirPath, null, null)
})
Notifier.updateStatus("kotlin-compiler.jar downloaded successfully")
return File(destDirPath + File.separator + kotlinCompilerJarName)
}
class CompilationException(s: String) : Exception()
private class CompilationException : Exception()
fun buildClasspath(cp: List<String>): String {
private fun buildClasspath(cp: List<String>): String {
if (SystemInfo.isWindows) {
val IDEA_JAR = "idea.jar"
val ideaJarPath = cp.find { pathStr -> pathStr.endsWith("${File.separator}$IDEA_JAR") }
val ideaLibPath = ideaJarPath!!.substring(startIndex = 0, endIndex = ideaJarPath.length - IDEA_JAR.length - File.separator.length)
val ideaJar = "idea.jar"
val ideaJarPath = cp.find { pathStr -> pathStr.endsWith("${File.separator}$ideaJar") }
val ideaLibPath = ideaJarPath!!.substring(startIndex = 0, endIndex = ideaJarPath.length - ideaJar.length - File.separator.length)
return cp.filterNot { pathStr -> pathStr.startsWith(ideaLibPath) }.plus(ideaLibPath).joinToString(";")
}
else return cp.joinToString(":")
}
private fun getProcessBuilderForWin(kotlinCompilerJar: File,
libDirLocation: File,
classpath: List<String>,
private fun getProcessBuilderForWin(kotlinCompilerJar: File, libDirLocation: File, classpath: List<String>,
scriptKt: File): ProcessBuilder {
val moduleXmlFile = createTempFile(
content = ModuleXmlBuilder.build(outputDir = tempDir.path, classPath = classpath, sourcePath = scriptKt.path), fileName = "module",
extension = ".xml")
return ProcessBuilder("java", "-jar",
kotlinCompilerJar.path,
"-kotlin-home", libDirLocation.path,
"-module", moduleXmlFile.path,
scriptKt.path)
return ProcessBuilder(
"java", "-jar", kotlinCompilerJar.path, "-kotlin-home", libDirLocation.path, "-module", moduleXmlFile.path, scriptKt.path)
}
}
@@ -20,14 +20,18 @@ package com.intellij.testGuiFramework.recorder.compile
*/
object ModuleXmlBuilder {
fun module(function: () -> String): String = "<modules>\n${function.invoke()}\n</modules>"
private fun module(function: () -> String): String = """<modules>
${function.invoke()}
</modules>"""
fun modules(outputDir: String,
function: () -> String): String = "<module name=\"main\" outputDir=\"$outputDir\" type=\"java-production\">\n${function.invoke()}\n</module>"
private fun modules(outputDir: String, function: () -> String): String =
"""<module name="main" outputDir="$outputDir" type="java-production">
${function.invoke()}
</module>"""
fun addSource(path: String) = "<sources path=\"$path\"/>"
private fun addSource(path: String) = """<sources path="$path"/>"""
fun addClasspath(path: String) = "<classpath path=\"$path\"/>"
private fun addClasspath(path: String) = """<classpath path="$path"/>"""
fun build(outputDir: String, classPath: List<String>, sourcePath: String) =