From 886dc4b438f798f517e8663c114787a6923b0ded Mon Sep 17 00:00:00 2001 From: Ilya Chernikov Date: Wed, 23 Sep 2015 13:20:15 +0200 Subject: [PATCH] Refactoring daemon tests for more reusable code, adding instances test, minor fixes and improvements --- .../org/jetbrains/kotlin/rmi/DaemonParams.kt | 9 +- .../kotlin/rmi/service/CompileDaemon.kt | 2 +- .../kotlin/daemon/CompilerDaemonTest.kt | 135 +++++++++++++----- 3 files changed, 104 insertions(+), 42 deletions(-) diff --git a/compiler/rmi/rmi-interface/src/org/jetbrains/kotlin/rmi/DaemonParams.kt b/compiler/rmi/rmi-interface/src/org/jetbrains/kotlin/rmi/DaemonParams.kt index b081521d78c..73c573edab1 100644 --- a/compiler/rmi/rmi-interface/src/org/jetbrains/kotlin/rmi/DaemonParams.kt +++ b/compiler/rmi/rmi-interface/src/org/jetbrains/kotlin/rmi/DaemonParams.kt @@ -285,7 +285,7 @@ public data class CompilerId( public fun isDaemonEnabled(): Boolean = System.getProperty(COMPILE_DAEMON_ENABLED_PROPERTY) != null -public fun configureDaemonJVMOptions(opts: DaemonJVMOptions, inheritMemoryLimits: Boolean): DaemonJVMOptions { +public fun configureDaemonJVMOptions(opts: DaemonJVMOptions, inheritMemoryLimits: Boolean, vararg additionalParams: String): DaemonJVMOptions { // note: sequence matters, explicit override in COMPILE_DAEMON_JVM_OPTIONS_PROPERTY should be done after inputArguments processing if (inheritMemoryLimits) { ManagementFactory.getRuntimeMXBean().inputArguments.filterExtractProps(opts.mappers, "-") @@ -300,13 +300,14 @@ public fun configureDaemonJVMOptions(opts: DaemonJVMOptions, inheritMemoryLimits System.getProperty(COMPILE_DAEMON_REPORT_PERF_PROPERTY)?.let { opts.jvmParams.add("D" + COMPILE_DAEMON_REPORT_PERF_PROPERTY) } System.getProperty(COMPILE_DAEMON_VERBOSE_REPORT_PROPERTY)?.let { opts.jvmParams.add("D" + COMPILE_DAEMON_VERBOSE_REPORT_PROPERTY) } - System.getProperty(COMPILE_DAEMON_LOG_PATH_PROPERTY)?.let { opts.jvmParams.add("D$COMPILE_DAEMON_LOG_PATH_PROPERTY=$it" ) } + System.getProperty(COMPILE_DAEMON_LOG_PATH_PROPERTY)?.let { opts.jvmParams.add("D$COMPILE_DAEMON_LOG_PATH_PROPERTY=\"$it\"" ) } + opts.jvmParams.addAll(additionalParams) return opts } -public fun configureDaemonJVMOptions(inheritMemoryLimits: Boolean): DaemonJVMOptions = - configureDaemonJVMOptions(DaemonJVMOptions(), inheritMemoryLimits = inheritMemoryLimits) +public fun configureDaemonJVMOptions(inheritMemoryLimits: Boolean, vararg additionalParams: String): DaemonJVMOptions = + configureDaemonJVMOptions(DaemonJVMOptions(), inheritMemoryLimits = inheritMemoryLimits, additionalParams = *additionalParams) public fun configureDaemonOptions(opts: DaemonOptions): DaemonOptions { diff --git a/compiler/rmi/rmi-server/src/org/jetbrains/kotlin/rmi/service/CompileDaemon.kt b/compiler/rmi/rmi-server/src/org/jetbrains/kotlin/rmi/service/CompileDaemon.kt index 201d5498f00..c27218e41f6 100644 --- a/compiler/rmi/rmi-server/src/org/jetbrains/kotlin/rmi/service/CompileDaemon.kt +++ b/compiler/rmi/rmi-server/src/org/jetbrains/kotlin/rmi/service/CompileDaemon.kt @@ -61,7 +61,7 @@ public object CompileDaemon { init { val logTime: String = SimpleDateFormat("yyyy-MM-dd.HH-mm-ss-SSS").format(Date()) val (logPath: String, fileIsGiven: Boolean) = - System.getProperty(COMPILE_DAEMON_LOG_PATH_PROPERTY)?.let { Pair(it, File(it).isFile) } ?: Pair("%t", false) + System.getProperty(COMPILE_DAEMON_LOG_PATH_PROPERTY)?.trimQuotes()?.let { Pair(it, File(it).isFile) } ?: Pair("%t", false) val cfg: String = "handlers = java.util.logging.FileHandler\n" + "java.util.logging.FileHandler.level = ALL\n" + diff --git a/compiler/tests/org/jetbrains/kotlin/daemon/CompilerDaemonTest.kt b/compiler/tests/org/jetbrains/kotlin/daemon/CompilerDaemonTest.kt index 96b306070e0..17df44c2cbd 100644 --- a/compiler/tests/org/jetbrains/kotlin/daemon/CompilerDaemonTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/daemon/CompilerDaemonTest.kt @@ -30,15 +30,11 @@ public class CompilerDaemonTest : KotlinIntegrationTestBase() { data class CompilerResults(val resultCode: Int, val out: String) - val daemonOptions by lazy(LazyThreadSafetyMode.NONE) { DaemonOptions(runFilesPath = tmpdir.absolutePath) } - val compilerId by lazy(LazyThreadSafetyMode.NONE) { - CompilerId.makeCompilerId( - File(KotlinIntegrationTestBase.getCompilerLib(), "kotlin-compiler.jar"), - File("dependencies/bootstrap-compiler/Kotlin/kotlinc/lib/kotlin-runtime.jar"), - File("dependencies/bootstrap-compiler/Kotlin/kotlinc/lib/kotlin-reflect.jar")) - } + val compilerClassPath = listOf( + File(KotlinIntegrationTestBase.getCompilerLib(), "kotlin-compiler.jar")) + val compilerId by lazy(LazyThreadSafetyMode.NONE) { CompilerId.makeCompilerId(compilerClassPath) } - private fun compileOnDaemon(daemonJVMOptions: DaemonJVMOptions, args: Array): CompilerResults { + private fun compileOnDaemon(compilerId: CompilerId, daemonJVMOptions: DaemonJVMOptions, daemonOptions: DaemonOptions, vararg args: String): CompilerResults { val daemon = KotlinCompilerClient.connectToCompileService(compilerId, daemonJVMOptions, daemonOptions, DaemonReportingTargets(out = System.err), autostart = true, checkId = true) TestCase.assertNotNull("failed to connect daemon", daemon) val strm = ByteArrayOutputStream() @@ -46,58 +42,68 @@ public class CompilerDaemonTest : KotlinIntegrationTestBase() { return CompilerResults(code, strm.toString()) } - private fun runDaemonCompilerTwice(logName: String, vararg arguments: String): Unit { - KotlinCompilerClient.shutdownCompileService(compilerId, daemonOptions) - - System.setProperty(COMPILE_DAEMON_VERBOSE_REPORT_PROPERTY, "") - val logFile = File.createTempFile("kotlin-daemon-test.", ".log") - System.setProperty(COMPILE_DAEMON_LOG_PATH_PROPERTY, logFile.absolutePath) - - val daemonJVMOptions = configureDaemonJVMOptions(false) - var daemonShotDown = false -// daemonJVMOptions.jvmParams.add("agentlib:jdwp=transport=dt_socket\\,server=y\\,suspend=y\\,address=5005") - - try { - val res1 = compileOnDaemon(daemonJVMOptions, arguments) + private fun runDaemonCompilerTwice(compilerId: CompilerId, daemonJVMOptions: DaemonJVMOptions, daemonOptions: DaemonOptions, vararg args: String): Unit { + val res1 = compileOnDaemon(compilerId, daemonJVMOptions, daemonOptions, *args) TestCase.assertEquals("first compilation failed:\n${res1.out}", 0, res1.resultCode) - val res2 = compileOnDaemon(daemonJVMOptions, arguments) + val res2 = compileOnDaemon(compilerId, daemonJVMOptions, daemonOptions, *args) TestCase.assertEquals("second compilation failed:\n${res2.out}", 0, res2.resultCode) TestCase.assertEquals("build results differ", CliBaseTest.removePerfOutput(res1.out), CliBaseTest.removePerfOutput(res2.out)) + } + + private fun getTestBaseDir(): String = JetTestUtils.getTestDataPathBase() + "/integration/smoke/" + getTestName(true) + private fun getHelloAppBaseDir(): String = JetTestUtils.getTestDataPathBase() + "/integration/smoke/helloApp" + + private fun run(logName: String, vararg args: String): Int = runJava(getTestBaseDir(), logName, *args) + + + public fun testHelloApp() { + val flagFile = createTempFile(getTestName(true), ".alive") + flagFile.deleteOnExit() + val daemonOptions = DaemonOptions(runFilesPath = File(tmpdir, getTestName(true)).absolutePath, + clientAliveFlagPath = flagFile.absolutePath) + + KotlinCompilerClient.shutdownCompileService(compilerId, daemonOptions) + + val logFile = createTempFile("kotlin-daemon-test.", ".log") + + val daemonJVMOptions = configureDaemonJVMOptions(false, + "D$COMPILE_DAEMON_VERBOSE_REPORT_PROPERTY", + "D$COMPILE_DAEMON_LOG_PATH_PROPERTY=\"${logFile.absolutePath}\"") + var daemonShotDown = false + + try { + val jar = tmpdir.absolutePath + File.separator + "hello.jar" + runDaemonCompilerTwice(compilerId, daemonJVMOptions, daemonOptions, + "-include-runtime", File(getTestBaseDir(), "hello.kt").absolutePath, "-d", jar) + KotlinCompilerClient.shutdownCompileService(compilerId, daemonOptions) daemonShotDown = true + var compileTime1 = 0L + var compileTime2 = 0L logFile.reader().useLines { it.ifNotContainsSequence( LinePattern("Kotlin compiler daemon version"), LinePattern("Starting compilation with args: "), + LinePattern("Elapsed time: (\\d+) ms", { it.groups.get(1)?.value?.toLong()?.let { compileTime1 = it }; true } ), LinePattern("Starting compilation with args: "), + LinePattern("Elapsed time: (\\d+) ms", { it.groups.get(1)?.value?.toLong()?.let { compileTime2 = it }; true } ), LinePattern("Shutdown complete")) - { (unmatchedPattern, lineNo) -> + { unmatchedPattern, lineNo -> TestCase.fail("pattern not found in the input: " + unmatchedPattern.regex + "\nunmatched part of the log file (" + logFile.absolutePath + ") from line " + lineNo + ":\n\n" + logFile.reader().useLines { it.drop(lineNo).joinToString("\n") }) } } + TestCase.assertTrue("Expecting that compilation 1 ($compileTime1 ms) is at least two times longer than compilation 2 ($compileTime2 ms)", + compileTime1 > compileTime2 * 2) logFile.delete() - // TODO: add performance comparison assert + run("hello.run", "-cp", jar, "Hello.HelloPackage") } finally { if (!daemonShotDown) KotlinCompilerClient.shutdownCompileService(compilerId, daemonOptions) - System.clearProperty(COMPILE_DAEMON_LOG_PATH_PROPERTY) - System.clearProperty(COMPILE_DAEMON_VERBOSE_REPORT_PROPERTY) } } - private fun getTestBaseDir(): String = JetTestUtils.getTestDataPathBase() + "/integration/smoke/" + getTestName(true) - - private fun run(logName: String, vararg args: String): Int = runJava(getTestBaseDir(), logName, *args) - - public fun testHelloApp() { - val jar = tmpdir.absolutePath + File.separator + "hello.jar" - - runDaemonCompilerTwice("hello.compile", "-include-runtime", File(getTestBaseDir(), "hello.kt").absolutePath, "-d", jar) - run("hello.run", "-cp", jar, "Hello.HelloPackage") - } - public fun testDaemonJvmOptionsParsing() { val backupJvmOptions = System.getProperty(COMPILE_DAEMON_JVM_OPTIONS_PROPERTY) try { @@ -126,6 +132,61 @@ public class CompilerDaemonTest : KotlinIntegrationTestBase() { restoreSystemProperty(COMPILE_DAEMON_OPTIONS_PROPERTY, backupOptions) } } + + public fun testDaemonInstances() { + val jar = tmpdir.absolutePath + File.separator + "hello1.jar" + val flagFile = createTempFile(getTestName(true), ".alive") + flagFile.deleteOnExit() + val daemonOptions = DaemonOptions(runFilesPath = File(tmpdir, getTestName(true)).absolutePath, + clientAliveFlagPath = flagFile.absolutePath) + val compilerId2 = CompilerId.makeCompilerId(compilerClassPath + + File(KotlinIntegrationTestBase.getCompilerLib(), "kotlin-compiler-sources.jar")) + + KotlinCompilerClient.shutdownCompileService(compilerId, daemonOptions) + KotlinCompilerClient.shutdownCompileService(compilerId2, daemonOptions) + + val logFile1 = createTempFile("kotlin-daemon1-test", ".log") + val logFile2 = createTempFile("kotlin-daemon2-test", ".log") + val daemonJVMOptions1 = + configureDaemonJVMOptions(false, + "D$COMPILE_DAEMON_LOG_PATH_PROPERTY=\"${logFile1.absolutePath}\"") + val daemonJVMOptions2 = + configureDaemonJVMOptions(false, + "D$COMPILE_DAEMON_LOG_PATH_PROPERTY=\"${logFile2.absolutePath}\"") + + TestCase.assertTrue(logFile1.length() == 0L && logFile2.length() == 0L) + + val res1 = compileOnDaemon(compilerId, daemonJVMOptions1, daemonOptions, + "-include-runtime", File(getHelloAppBaseDir(), "hello.kt").absolutePath, "-d", jar) + TestCase.assertEquals("first compilation failed:\n${res1.out}", 0, res1.resultCode) + + logFile1.assertLogContainsSequence("Starting compilation with args: ") + TestCase.assertEquals("expecting '${logFile2.absolutePath}' to be empty", 0L, logFile2.length()) + + val res2 = compileOnDaemon(compilerId2, daemonJVMOptions2, daemonOptions, + "-include-runtime", File(getHelloAppBaseDir(), "hello.kt").absolutePath, "-d", jar) + TestCase.assertEquals("second compilation failed:\n${res2.out}", 0, res1.resultCode) + + logFile2.assertLogContainsSequence("Starting compilation with args: ") + + KotlinCompilerClient.shutdownCompileService(compilerId, daemonOptions) + logFile1.assertLogContainsSequence("Shutdown complete") + logFile1.delete() + + KotlinCompilerClient.shutdownCompileService(compilerId2, daemonOptions) + logFile2.assertLogContainsSequence("Shutdown complete") + logFile2.delete() + } +} + + +fun File.assertLogContainsSequence(vararg patterns: String) { + reader().useLines { + it.ifNotContainsSequence( patterns.map { LinePattern(it) }) + { + pattern,lineNo -> TestCase.fail("Pattern '${pattern.regex}' is not found in the log file '$absolutePath'") + } + } } fun restoreSystemProperty(propertyName: String, backupValue: String?) {