Refactor daemon tests, some minor fixes in the client and daemon
Tests refactored for better readability and code reuse Some shutdown livelock is fixed Some logging added
This commit is contained in:
+3
@@ -145,6 +145,7 @@ object KotlinCompilerClient {
|
|||||||
compilerService.releaseCompileSession(sessionId)
|
compilerService.releaseCompileSession(sessionId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated("Use other compile method", ReplaceWith("compile"))
|
||||||
fun compile(compilerService: CompileService,
|
fun compile(compilerService: CompileService,
|
||||||
sessionId: Int,
|
sessionId: Int,
|
||||||
targetPlatform: CompileService.TargetPlatform,
|
targetPlatform: CompileService.TargetPlatform,
|
||||||
@@ -158,6 +159,7 @@ object KotlinCompilerClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Deprecated("Use non-deprecated compile method", ReplaceWith("compile"))
|
||||||
fun incrementalCompile(compileService: CompileService,
|
fun incrementalCompile(compileService: CompileService,
|
||||||
sessionId: Int,
|
sessionId: Int,
|
||||||
targetPlatform: CompileService.TargetPlatform,
|
targetPlatform: CompileService.TargetPlatform,
|
||||||
@@ -308,6 +310,7 @@ object KotlinCompilerClient {
|
|||||||
|
|
||||||
// --- Implementation ---------------------------------------
|
// --- Implementation ---------------------------------------
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
private inline fun <R> connectLoop(reportingTargets: DaemonReportingTargets, body: () -> R?): R? {
|
private inline fun <R> connectLoop(reportingTargets: DaemonReportingTargets, body: () -> R?): R? {
|
||||||
try {
|
try {
|
||||||
var attempts = 0
|
var attempts = 0
|
||||||
|
|||||||
@@ -335,6 +335,7 @@ class CompileServiceImpl(
|
|||||||
val daemonReporter = DaemonMessageReporter(servicesFacade, compilationOptions)
|
val daemonReporter = DaemonMessageReporter(servicesFacade, compilationOptions)
|
||||||
val compilerMode = compilationOptions.compilerMode
|
val compilerMode = compilationOptions.compilerMode
|
||||||
val targetPlatform = compilationOptions.targetPlatform
|
val targetPlatform = compilationOptions.targetPlatform
|
||||||
|
log.info("Starting compilation with args: " + compilerArguments.joinToString(" "))
|
||||||
val k2PlatformArgs = try {
|
val k2PlatformArgs = try {
|
||||||
when (targetPlatform) {
|
when (targetPlatform) {
|
||||||
CompileService.TargetPlatform.JVM -> K2JVMCompilerArguments().apply { K2JVMCompiler().parseArguments(compilerArguments, this) }
|
CompileService.TargetPlatform.JVM -> K2JVMCompilerArguments().apply { K2JVMCompiler().parseArguments(compilerArguments, this) }
|
||||||
@@ -709,6 +710,10 @@ class CompileServiceImpl(
|
|||||||
|
|
||||||
private fun shutdownImpl() {
|
private fun shutdownImpl() {
|
||||||
log.info("Shutdown started")
|
log.info("Shutdown started")
|
||||||
|
fun Long.mb() = this / (1024 * 1024)
|
||||||
|
with (Runtime.getRuntime()) {
|
||||||
|
log.info("Memory stats: total: ${totalMemory().mb()}mb, free: ${freeMemory().mb()}mb, max: ${maxMemory().mb()}mb")
|
||||||
|
}
|
||||||
state.alive.set(Aliveness.Dying.ordinal)
|
state.alive.set(Aliveness.Dying.ordinal)
|
||||||
|
|
||||||
UnicastRemoteObject.unexportObject(this, true)
|
UnicastRemoteObject.unexportObject(this, true)
|
||||||
@@ -869,9 +874,18 @@ class CompileServiceImpl(
|
|||||||
ifAliveChecksImpl(minAliveness, ignoreCompilerChanged, body)
|
ifAliveChecksImpl(minAliveness, ignoreCompilerChanged, body)
|
||||||
}
|
}
|
||||||
|
|
||||||
inline private fun<R> ifAliveChecksImpl(minAliveness: Aliveness = Aliveness.Alive, ignoreCompilerChanged: Boolean = false, body: () -> CompileService.CallResult<R>): CompileService.CallResult<R> =
|
inline private fun<R> ifAliveChecksImpl(minAliveness: Aliveness = Aliveness.Alive, ignoreCompilerChanged: Boolean = false, body: () -> CompileService.CallResult<R>): CompileService.CallResult<R> {
|
||||||
when {
|
val curState = state.alive.get()
|
||||||
state.alive.get() < minAliveness.ordinal -> CompileService.CallResult.Dying()
|
return when {
|
||||||
|
curState < minAliveness.ordinal -> {
|
||||||
|
log.info("Cannot perform operation, requested state: ${minAliveness.name} > actual: ${try {
|
||||||
|
Aliveness.values()[curState].name
|
||||||
|
}
|
||||||
|
catch (_: Throwable) {
|
||||||
|
"invalid($curState)"
|
||||||
|
}}")
|
||||||
|
CompileService.CallResult.Dying()
|
||||||
|
}
|
||||||
!ignoreCompilerChanged && classpathWatcher.isChanged -> {
|
!ignoreCompilerChanged && classpathWatcher.isChanged -> {
|
||||||
log.info("Compiler changed, scheduling shutdown")
|
log.info("Compiler changed, scheduling shutdown")
|
||||||
shutdownWithDelay()
|
shutdownWithDelay()
|
||||||
@@ -887,6 +901,7 @@ class CompileServiceImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private inline fun<R> withValidClientOrSessionProxy(sessionId: Int,
|
private inline fun<R> withValidClientOrSessionProxy(sessionId: Int,
|
||||||
body: (ClientOrSessionProxy<Any>?) -> CompileService.CallResult<R>
|
body: (ClientOrSessionProxy<Any>?) -> CompileService.CallResult<R>
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ package org.jetbrains.kotlin.daemon
|
|||||||
|
|
||||||
import junit.framework.TestCase
|
import junit.framework.TestCase
|
||||||
import org.jetbrains.kotlin.cli.AbstractCliTest
|
import org.jetbrains.kotlin.cli.AbstractCliTest
|
||||||
|
import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
|
||||||
|
import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector
|
||||||
import org.jetbrains.kotlin.cli.common.repl.*
|
import org.jetbrains.kotlin.cli.common.repl.*
|
||||||
import org.jetbrains.kotlin.daemon.client.*
|
import org.jetbrains.kotlin.daemon.client.*
|
||||||
import org.jetbrains.kotlin.daemon.common.*
|
import org.jetbrains.kotlin.daemon.common.*
|
||||||
@@ -26,6 +28,7 @@ import org.jetbrains.kotlin.progress.CompilationCanceledStatus
|
|||||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.io.PrintStream
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.net.URLClassLoader
|
import java.net.URLClassLoader
|
||||||
import java.rmi.server.UnicastRemoteObject
|
import java.rmi.server.UnicastRemoteObject
|
||||||
@@ -58,7 +61,9 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
|
|||||||
assertNotNull("failed to connect daemon", daemon)
|
assertNotNull("failed to connect daemon", daemon)
|
||||||
daemon?.registerClient(clientAliveFile.absolutePath)
|
daemon?.registerClient(clientAliveFile.absolutePath)
|
||||||
val strm = ByteArrayOutputStream()
|
val strm = ByteArrayOutputStream()
|
||||||
val code = KotlinCompilerClient.compile(daemon!!, CompileService.NO_SESSION, CompileService.TargetPlatform.JVM, args, strm)
|
val code = KotlinCompilerClient.compile(daemon!!, CompileService.NO_SESSION, CompileService.TargetPlatform.JVM,
|
||||||
|
args, PrintingMessageCollector(PrintStream(strm), MessageRenderer.WITHOUT_PATHS, true),
|
||||||
|
reportSeverity = ReportSeverity.DEBUG)
|
||||||
return CompilerResults(code, strm.toString())
|
return CompilerResults(code, strm.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,19 +80,33 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
|
|||||||
|
|
||||||
private fun run(logName: String, vararg args: String): Int = runJava(getTestBaseDir(), logName, *args)
|
private fun run(logName: String, vararg args: String): Int = runJava(getTestBaseDir(), logName, *args)
|
||||||
|
|
||||||
fun testHelloApp() {
|
fun makeTestDaemonOptions(testName: String, shutdownDelay: Int = 5) =
|
||||||
withFlagFile(getTestName(true), ".alive") { flagFile ->
|
DaemonOptions(runFilesPath = File(tmpdir, testName).absolutePath,
|
||||||
val daemonOptions = DaemonOptions(runFilesPath = File(tmpdir, getTestName(true)).absolutePath,
|
shutdownDelayMilliseconds = shutdownDelay.toLong(),
|
||||||
shutdownDelayMilliseconds = 1,
|
|
||||||
verbose = true,
|
verbose = true,
|
||||||
reportPerf = true)
|
reportPerf = true)
|
||||||
|
|
||||||
|
fun makeTestDaemonJvmOptions(logFile: File? = null, xmx: Int = 256): DaemonJVMOptions {
|
||||||
|
val additionalArgs = arrayListOf<String>()
|
||||||
|
if (logFile != null) {
|
||||||
|
additionalArgs.add("D$COMPILE_DAEMON_LOG_PATH_PROPERTY=\"${logFile.loggerCompatiblePath}\"")
|
||||||
|
}
|
||||||
|
val baseOpts = if (xmx > 0) DaemonJVMOptions(maxMemory = "${xmx}m") else DaemonJVMOptions()
|
||||||
|
return configureDaemonJVMOptions(
|
||||||
|
baseOpts,
|
||||||
|
*additionalArgs.toTypedArray(),
|
||||||
|
inheritMemoryLimits = xmx > 0,
|
||||||
|
inheritAdditionalProperties = false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testHelloApp() {
|
||||||
|
withFlagFile(getTestName(true), ".alive") { flagFile ->
|
||||||
|
val daemonOptions = makeTestDaemonOptions(getTestName(true))
|
||||||
|
|
||||||
KotlinCompilerClient.shutdownCompileService(compilerId, daemonOptions)
|
KotlinCompilerClient.shutdownCompileService(compilerId, daemonOptions)
|
||||||
|
|
||||||
val logFile = createTempFile("kotlin-daemon-test.", ".log")
|
withLogFile("kotlin-daemon-test") { logFile ->
|
||||||
|
val daemonJVMOptions = makeTestDaemonJvmOptions(logFile)
|
||||||
val daemonJVMOptions = configureDaemonJVMOptions("D$COMPILE_DAEMON_LOG_PATH_PROPERTY=\"${logFile.loggerCompatiblePath}\"",
|
|
||||||
inheritMemoryLimits = false, inheritAdditionalProperties = false)
|
|
||||||
var daemonShotDown = false
|
var daemonShotDown = false
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -118,6 +137,7 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun testDaemonJvmOptionsParsing() {
|
fun testDaemonJvmOptionsParsing() {
|
||||||
val backupJvmOptions = System.getProperty(COMPILE_DAEMON_JVM_OPTIONS_PROPERTY)
|
val backupJvmOptions = System.getProperty(COMPILE_DAEMON_JVM_OPTIONS_PROPERTY)
|
||||||
@@ -149,22 +169,17 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
|
|||||||
|
|
||||||
fun testDaemonInstancesSimple() {
|
fun testDaemonInstancesSimple() {
|
||||||
withFlagFile(getTestName(true), ".alive") { flagFile ->
|
withFlagFile(getTestName(true), ".alive") { flagFile ->
|
||||||
val daemonOptions = DaemonOptions(shutdownDelayMilliseconds = 1, runFilesPath = File(tmpdir, getTestName(true)).absolutePath)
|
val daemonOptions = makeTestDaemonOptions(getTestName(true))
|
||||||
val compilerId2 = CompilerId.makeCompilerId(compilerClassPath +
|
val compilerId2 = CompilerId.makeCompilerId(compilerClassPath +
|
||||||
File(KotlinIntegrationTestBase.getCompilerLib(), "kotlin-compiler-sources.jar"))
|
File(KotlinIntegrationTestBase.getCompilerLib(), "kotlin-compiler-sources.jar"))
|
||||||
|
|
||||||
KotlinCompilerClient.shutdownCompileService(compilerId, daemonOptions)
|
KotlinCompilerClient.shutdownCompileService(compilerId, daemonOptions)
|
||||||
KotlinCompilerClient.shutdownCompileService(compilerId2, daemonOptions)
|
KotlinCompilerClient.shutdownCompileService(compilerId2, daemonOptions)
|
||||||
|
|
||||||
val logFile1 = createTempFile("kotlin-daemon1-test", ".log")
|
withLogFile("kotlin-daemon1-test") { logFile1 ->
|
||||||
val logFile2 = createTempFile("kotlin-daemon2-test", ".log")
|
withLogFile("kotlin-daemon2-test") { logFile2 ->
|
||||||
val daemonJVMOptions1 =
|
val daemonJVMOptions1 = makeTestDaemonJvmOptions(logFile1)
|
||||||
configureDaemonJVMOptions("D$COMPILE_DAEMON_LOG_PATH_PROPERTY=\"${logFile1.loggerCompatiblePath}\"",
|
val daemonJVMOptions2 = makeTestDaemonJvmOptions(logFile2)
|
||||||
inheritMemoryLimits = false, inheritAdditionalProperties = false)
|
|
||||||
|
|
||||||
val daemonJVMOptions2 =
|
|
||||||
configureDaemonJVMOptions("D$COMPILE_DAEMON_LOG_PATH_PROPERTY=\"${logFile2.loggerCompatiblePath}\"",
|
|
||||||
inheritMemoryLimits = false, inheritAdditionalProperties = false)
|
|
||||||
|
|
||||||
assertTrue(logFile1.length() == 0L && logFile2.length() == 0L)
|
assertTrue(logFile1.length() == 0L && logFile2.length() == 0L)
|
||||||
|
|
||||||
@@ -190,16 +205,16 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
|
|||||||
logFile2.assertLogContainsSequence("Shutdown started")
|
logFile2.assertLogContainsSequence("Shutdown started")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun testDaemonAutoshutdownOnUnused() {
|
fun testDaemonAutoshutdownOnUnused() {
|
||||||
withFlagFile(getTestName(true), ".alive") { flagFile ->
|
withFlagFile(getTestName(true), ".alive") { flagFile ->
|
||||||
val daemonOptions = DaemonOptions(autoshutdownUnusedSeconds = 1, shutdownDelayMilliseconds = 1, runFilesPath = File(tmpdir, getTestName(true)).absolutePath)
|
val daemonOptions = DaemonOptions(autoshutdownUnusedSeconds = 1, shutdownDelayMilliseconds = 1, runFilesPath = File(tmpdir, getTestName(true)).absolutePath)
|
||||||
KotlinCompilerClient.shutdownCompileService(compilerId, daemonOptions)
|
KotlinCompilerClient.shutdownCompileService(compilerId, daemonOptions)
|
||||||
|
|
||||||
val logFile = createTempFile("kotlin-daemon-test", ".log")
|
withLogFile("kotlin-daemon-test") { logFile ->
|
||||||
val daemonJVMOptions =
|
val daemonJVMOptions = makeTestDaemonJvmOptions(logFile)
|
||||||
configureDaemonJVMOptions("D$COMPILE_DAEMON_LOG_PATH_PROPERTY=\"${logFile.loggerCompatiblePath}\"",
|
|
||||||
inheritMemoryLimits = false, inheritAdditionalProperties = false)
|
|
||||||
|
|
||||||
val daemon = KotlinCompilerClient.connectToCompileService(compilerId, flagFile, daemonJVMOptions, daemonOptions, DaemonReportingTargets(out = System.err), autostart = true)
|
val daemon = KotlinCompilerClient.connectToCompileService(compilerId, flagFile, daemonJVMOptions, daemonOptions, DaemonReportingTargets(out = System.err), autostart = true)
|
||||||
assertNotNull("failed to connect daemon", daemon)
|
assertNotNull("failed to connect daemon", daemon)
|
||||||
@@ -214,7 +229,7 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
|
|||||||
|
|
||||||
logFile.assertLogContainsSequence("Unused timeout exceeded 1s",
|
logFile.assertLogContainsSequence("Unused timeout exceeded 1s",
|
||||||
"Shutdown started")
|
"Shutdown started")
|
||||||
logFile.delete()
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,17 +238,18 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
|
|||||||
val daemonOptions = DaemonOptions(autoshutdownIdleSeconds = 1, shutdownDelayMilliseconds = 1, runFilesPath = File(tmpdir, getTestName(true)).absolutePath)
|
val daemonOptions = DaemonOptions(autoshutdownIdleSeconds = 1, shutdownDelayMilliseconds = 1, runFilesPath = File(tmpdir, getTestName(true)).absolutePath)
|
||||||
KotlinCompilerClient.shutdownCompileService(compilerId, daemonOptions)
|
KotlinCompilerClient.shutdownCompileService(compilerId, daemonOptions)
|
||||||
|
|
||||||
val logFile = createTempFile("kotlin-daemon-test", ".log")
|
withLogFile("kotlin-daemon-test") { logFile ->
|
||||||
val daemonJVMOptions =
|
val daemonJVMOptions = makeTestDaemonJvmOptions(logFile)
|
||||||
configureDaemonJVMOptions("D$COMPILE_DAEMON_LOG_PATH_PROPERTY=\"${logFile.loggerCompatiblePath}\"",
|
|
||||||
inheritMemoryLimits = false, inheritAdditionalProperties = false)
|
|
||||||
|
|
||||||
val daemon = KotlinCompilerClient.connectToCompileService(compilerId, flagFile, daemonJVMOptions, daemonOptions, DaemonReportingTargets(out = System.err), autostart = true)
|
val daemon = KotlinCompilerClient.connectToCompileService(compilerId, flagFile, daemonJVMOptions, daemonOptions, DaemonReportingTargets(out = System.err), autostart = true)
|
||||||
assertNotNull("failed to connect daemon", daemon)
|
assertNotNull("failed to connect daemon", daemon)
|
||||||
daemon?.registerClient(flagFile.absolutePath)
|
daemon?.registerClient(flagFile.absolutePath)
|
||||||
val jar = tmpdir.absolutePath + File.separator + "hello1.jar"
|
val jar = tmpdir.absolutePath + File.separator + "hello1.jar"
|
||||||
val strm = ByteArrayOutputStream()
|
val strm = ByteArrayOutputStream()
|
||||||
val code = KotlinCompilerClient.compile(daemon!!, CompileService.NO_SESSION, CompileService.TargetPlatform.JVM, arrayOf("-include-runtime", File(getHelloAppBaseDir(), "hello.kt").absolutePath, "-d", jar), strm)
|
val code = KotlinCompilerClient.compile(daemon!!, CompileService.NO_SESSION, CompileService.TargetPlatform.JVM,
|
||||||
|
arrayOf("-include-runtime", File(getHelloAppBaseDir(), "hello.kt").absolutePath, "-d", jar),
|
||||||
|
PrintingMessageCollector(PrintStream(strm), MessageRenderer.WITHOUT_PATHS, true),
|
||||||
|
reportSeverity = ReportSeverity.DEBUG)
|
||||||
assertEquals("compilation failed:\n$strm", 0, code)
|
assertEquals("compilation failed:\n$strm", 0, code)
|
||||||
|
|
||||||
logFile.assertLogContainsSequence("Starting compilation with args: ")
|
logFile.assertLogContainsSequence("Starting compilation with args: ")
|
||||||
@@ -246,7 +262,7 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
|
|||||||
Thread.sleep(200)
|
Thread.sleep(200)
|
||||||
logFile.assertLogContainsSequence("Idle timeout exceeded 1s",
|
logFile.assertLogContainsSequence("Idle timeout exceeded 1s",
|
||||||
"Shutdown started")
|
"Shutdown started")
|
||||||
logFile.delete()
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -255,10 +271,8 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
|
|||||||
val daemonOptions = DaemonOptions(autoshutdownIdleSeconds = 1, shutdownDelayMilliseconds = 1, forceShutdownTimeoutMilliseconds = 60000, runFilesPath = File(tmpdir, getTestName(true)).absolutePath)
|
val daemonOptions = DaemonOptions(autoshutdownIdleSeconds = 1, shutdownDelayMilliseconds = 1, forceShutdownTimeoutMilliseconds = 60000, runFilesPath = File(tmpdir, getTestName(true)).absolutePath)
|
||||||
KotlinCompilerClient.shutdownCompileService(compilerId, daemonOptions)
|
KotlinCompilerClient.shutdownCompileService(compilerId, daemonOptions)
|
||||||
|
|
||||||
val logFile = createTempFile("kotlin-daemon-test", ".log")
|
withLogFile("kotlin-daemon-test") { logFile ->
|
||||||
val daemonJVMOptions =
|
val daemonJVMOptions = makeTestDaemonJvmOptions(logFile)
|
||||||
configureDaemonJVMOptions("D$COMPILE_DAEMON_LOG_PATH_PROPERTY=\"${logFile.loggerCompatiblePath}\"",
|
|
||||||
inheritMemoryLimits = false, inheritAdditionalProperties = false)
|
|
||||||
|
|
||||||
val daemon = KotlinCompilerClient.connectToCompileService(compilerId, flagFile, daemonJVMOptions, daemonOptions, DaemonReportingTargets(out = System.err), autostart = true)
|
val daemon = KotlinCompilerClient.connectToCompileService(compilerId, flagFile, daemonJVMOptions, daemonOptions, DaemonReportingTargets(out = System.err), autostart = true)
|
||||||
assertNotNull("failed to connect daemon", daemon)
|
assertNotNull("failed to connect daemon", daemon)
|
||||||
@@ -267,7 +281,7 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
|
|||||||
|
|
||||||
val scheduleShutdownRes = daemon?.scheduleShutdown(true)
|
val scheduleShutdownRes = daemon?.scheduleShutdown(true)
|
||||||
|
|
||||||
assertTrue("failed to schedule shutdown ($scheduleShutdownRes)", scheduleShutdownRes?.let { it.isGood && it.get() } ?: false )
|
assertTrue("failed to schedule shutdown ($scheduleShutdownRes)", scheduleShutdownRes?.let { it.isGood && it.get() } ?: false)
|
||||||
|
|
||||||
Thread.sleep(100) // to allow timer task to run in the daemon
|
Thread.sleep(100) // to allow timer task to run in the daemon
|
||||||
|
|
||||||
@@ -283,7 +297,7 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
|
|||||||
|
|
||||||
logFile.assertLogContainsSequence("All sessions finished, shutting down",
|
logFile.assertLogContainsSequence("All sessions finished, shutting down",
|
||||||
"Shutdown started")
|
"Shutdown started")
|
||||||
logFile.delete()
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -299,11 +313,11 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
|
|||||||
*/
|
*/
|
||||||
fun testDaemonExecutionViaIntermediateProcess() {
|
fun testDaemonExecutionViaIntermediateProcess() {
|
||||||
val clientAliveFile = createTempFile("kotlin-daemon-transitive-run-test", ".run")
|
val clientAliveFile = createTempFile("kotlin-daemon-transitive-run-test", ".run")
|
||||||
val runFilesPath = File(tmpdir, getTestName(true)).absolutePath
|
val daemonOptions = makeTestDaemonOptions(getTestName(true))
|
||||||
val daemonOptions = DaemonOptions(shutdownDelayMilliseconds = 1, runFilesPath = runFilesPath)
|
|
||||||
val jar = tmpdir.absolutePath + File.separator + "hello.jar"
|
val jar = tmpdir.absolutePath + File.separator + "hello.jar"
|
||||||
val args = listOf(
|
val args = listOf(
|
||||||
File(File(System.getProperty("java.home"), "bin"), "java").absolutePath,
|
File(File(System.getProperty("java.home"), "bin"), "java").absolutePath,
|
||||||
|
"-Xmx256m",
|
||||||
"-D$COMPILE_DAEMON_VERBOSE_REPORT_PROPERTY",
|
"-D$COMPILE_DAEMON_VERBOSE_REPORT_PROPERTY",
|
||||||
"-cp",
|
"-cp",
|
||||||
daemonClientClassPath.joinToString(File.pathSeparator) { it.absolutePath },
|
daemonClientClassPath.joinToString(File.pathSeparator) { it.absolutePath },
|
||||||
@@ -354,14 +368,15 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
|
|||||||
assertTrue(PARALLEL_THREADS_TO_COMPILE <= LoopbackNetworkInterface.SERVER_SOCKET_BACKLOG_SIZE)
|
assertTrue(PARALLEL_THREADS_TO_COMPILE <= LoopbackNetworkInterface.SERVER_SOCKET_BACKLOG_SIZE)
|
||||||
|
|
||||||
withFlagFile(getTestName(true), ".alive") { flagFile ->
|
withFlagFile(getTestName(true), ".alive") { flagFile ->
|
||||||
val daemonOptions = DaemonOptions(shutdownDelayMilliseconds = 1, runFilesPath = File(tmpdir, getTestName(true)).absolutePath)
|
val daemonOptions = makeTestDaemonOptions(getTestName(true))
|
||||||
val daemonJVMOptions = configureDaemonJVMOptions(inheritMemoryLimits = true, inheritAdditionalProperties = false)
|
|
||||||
|
withLogFile("kotlin-daemon-test") { logFile ->
|
||||||
|
val daemonJVMOptions = makeTestDaemonJvmOptions(logFile, xmx = -1)
|
||||||
|
|
||||||
val daemon = KotlinCompilerClient.connectToCompileService(compilerId, flagFile, daemonJVMOptions, daemonOptions, DaemonReportingTargets(out = System.err), autostart = true)
|
val daemon = KotlinCompilerClient.connectToCompileService(compilerId, flagFile, daemonJVMOptions, daemonOptions, DaemonReportingTargets(out = System.err), autostart = true)
|
||||||
assertNotNull("failed to connect daemon", daemon)
|
assertNotNull("failed to connect daemon", daemon)
|
||||||
|
|
||||||
val (_, port) = findPortAndCreateRegistry(10, 16384, 65535)
|
val (_, port) = findPortAndCreateRegistry(10, 16384, 65535)
|
||||||
val tracer = SynchronizationTracer(CountDownLatch(1), CountDownLatch(PARALLEL_THREADS_TO_COMPILE), port)
|
|
||||||
|
|
||||||
val resultCodes = arrayOfNulls<Int>(PARALLEL_THREADS_TO_COMPILE)
|
val resultCodes = arrayOfNulls<Int>(PARALLEL_THREADS_TO_COMPILE)
|
||||||
val localEndSignal = CountDownLatch(PARALLEL_THREADS_TO_COMPILE)
|
val localEndSignal = CountDownLatch(PARALLEL_THREADS_TO_COMPILE)
|
||||||
val outStreams = Array(PARALLEL_THREADS_TO_COMPILE, { ByteArrayOutputStream() })
|
val outStreams = Array(PARALLEL_THREADS_TO_COMPILE, { ByteArrayOutputStream() })
|
||||||
@@ -374,9 +389,8 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
|
|||||||
CompileService.NO_SESSION,
|
CompileService.NO_SESSION,
|
||||||
CompileService.TargetPlatform.JVM,
|
CompileService.TargetPlatform.JVM,
|
||||||
arrayOf("-include-runtime", File(getHelloAppBaseDir(), "hello.kt").absolutePath, "-d", jar),
|
arrayOf("-include-runtime", File(getHelloAppBaseDir(), "hello.kt").absolutePath, "-d", jar),
|
||||||
outStreams[threadNo],
|
PrintingMessageCollector(PrintStream(outStreams[threadNo]), MessageRenderer.WITHOUT_PATHS, true),
|
||||||
port = port,
|
port = port)
|
||||||
operationsTracer = tracer as RemoteOperationsTracer)
|
|
||||||
synchronized(resultCodes) {
|
synchronized(resultCodes) {
|
||||||
resultCodes[threadNo] = res
|
resultCodes[threadNo] = res
|
||||||
}
|
}
|
||||||
@@ -385,43 +399,38 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
|
|||||||
|
|
||||||
(1..PARALLEL_THREADS_TO_COMPILE).forEach { runCompile(it - 1) }
|
(1..PARALLEL_THREADS_TO_COMPILE).forEach { runCompile(it - 1) }
|
||||||
|
|
||||||
tracer.startSignal.countDown()
|
val succeeded = localEndSignal.await(PARALLEL_WAIT_TIMEOUT_S, TimeUnit.SECONDS)
|
||||||
val succeeded = tracer.doneSignal.await(PARALLEL_WAIT_TIMEOUT_S, TimeUnit.SECONDS)
|
assertTrue("parallel compilation failed to complete in $PARALLEL_WAIT_TIMEOUT_S s, ${localEndSignal.count} unfinished threads", succeeded)
|
||||||
assertTrue("parallel compilation failed to complete in $PARALLEL_WAIT_TIMEOUT_S s, ${tracer.doneSignal.count} unfinished threads", succeeded)
|
|
||||||
|
|
||||||
localEndSignal.await(PARALLEL_WAIT_TIMEOUT_S, TimeUnit.SECONDS)
|
|
||||||
(1..PARALLEL_THREADS_TO_COMPILE).forEach {
|
(1..PARALLEL_THREADS_TO_COMPILE).forEach {
|
||||||
assertEquals("Compilation on thread $it failed:\n${outStreams[it - 1]}", 0, resultCodes[it - 1])
|
assertEquals("Compilation on thread $it failed:\n${outStreams[it - 1]}", 0, resultCodes[it - 1])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val PARALLEL_THREADS_TO_START = 8
|
||||||
|
|
||||||
fun testParallelDaemonStart() {
|
fun testParallelDaemonStart() {
|
||||||
|
|
||||||
val PARALLEL_THREADS_TO_START = 8
|
|
||||||
|
|
||||||
val doneLatch = CountDownLatch(PARALLEL_THREADS_TO_START)
|
val doneLatch = CountDownLatch(PARALLEL_THREADS_TO_START)
|
||||||
|
|
||||||
val resultCodes = arrayOfNulls<Int>(PARALLEL_THREADS_TO_START)
|
val resultCodes = arrayOfNulls<Int>(PARALLEL_THREADS_TO_START)
|
||||||
val outStreams = Array(PARALLEL_THREADS_TO_START, { ByteArrayOutputStream() })
|
val outStreams = Array(PARALLEL_THREADS_TO_START, { ByteArrayOutputStream() })
|
||||||
val logFiles = arrayOfNulls<File>(PARALLEL_THREADS_TO_START)
|
val logFiles = arrayOfNulls<File>(PARALLEL_THREADS_TO_START)
|
||||||
|
val daemonOptions = makeTestDaemonOptions(getTestName(true), 100)
|
||||||
|
KotlinCompilerClient.shutdownCompileService(compilerId, daemonOptions)
|
||||||
|
|
||||||
fun connectThread(threadNo: Int) = thread(name = "daemonConnect$threadNo") {
|
fun connectThread(threadNo: Int) = thread(name = "daemonConnect$threadNo") {
|
||||||
try {
|
try {
|
||||||
withFlagFile(getTestName(true), ".alive") { flagFile ->
|
withFlagFile(getTestName(true), ".alive") { flagFile ->
|
||||||
withFlagFile(getTestName(true), ".salive") { sessionFlagFile ->
|
withLogFile("kotlin-daemon-test") { logFile ->
|
||||||
val daemonOptions = DaemonOptions(shutdownDelayMilliseconds = 10000, // attempt to avoid immediate shutdown of the non-elected daemons
|
|
||||||
runFilesPath = File(tmpdir, getTestName(true)).absolutePath,
|
|
||||||
verbose = true)
|
|
||||||
val logFile = createTempFile("kotlin-daemon-test", ".log")
|
|
||||||
logFiles[threadNo] = logFile
|
logFiles[threadNo] = logFile
|
||||||
val daemonJVMOptions =
|
val daemonJVMOptions = makeTestDaemonJvmOptions(logFile)
|
||||||
configureDaemonJVMOptions(DaemonJVMOptions(maxMemory = "2048m"),
|
val compileServiceSession =
|
||||||
"D$COMPILE_DAEMON_LOG_PATH_PROPERTY=\"${logFile.loggerCompatiblePath}\"",
|
KotlinCompilerClient.connectAndLease(compilerId, flagFile, daemonJVMOptions, daemonOptions,
|
||||||
inheritMemoryLimits = false, inheritAdditionalProperties = false)
|
|
||||||
val compileServiceSession = KotlinCompilerClient.connectAndLease(compilerId, flagFile, daemonJVMOptions, daemonOptions,
|
|
||||||
DaemonReportingTargets(out = System.err), autostart = true,
|
DaemonReportingTargets(out = System.err), autostart = true,
|
||||||
leaseSession = true, sessionAliveFlagFile = sessionFlagFile)
|
leaseSession = true)
|
||||||
assertNotNull("failed to connect daemon", compileServiceSession?.compileService)
|
assertNotNull("failed to connect daemon", compileServiceSession?.compileService)
|
||||||
val jar = tmpdir.absolutePath + File.separator + "hello.$threadNo.jar"
|
val jar = tmpdir.absolutePath + File.separator + "hello.$threadNo.jar"
|
||||||
val res = KotlinCompilerClient.compile(
|
val res = KotlinCompilerClient.compile(
|
||||||
@@ -429,8 +438,9 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
|
|||||||
compileServiceSession.sessionId,
|
compileServiceSession.sessionId,
|
||||||
CompileService.TargetPlatform.JVM,
|
CompileService.TargetPlatform.JVM,
|
||||||
arrayOf(File(getHelloAppBaseDir(), "hello.kt").absolutePath, "-d", jar),
|
arrayOf(File(getHelloAppBaseDir(), "hello.kt").absolutePath, "-d", jar),
|
||||||
outStreams[threadNo])
|
PrintingMessageCollector(PrintStream(outStreams[threadNo]), MessageRenderer.WITHOUT_PATHS, true))
|
||||||
resultCodes[threadNo] = res
|
resultCodes[threadNo] = res
|
||||||
|
assertEquals("Compilation on thread $threadNo failed", 0, res)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -457,22 +467,19 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
|
|||||||
|
|
||||||
val electionLogs = (0..(PARALLEL_THREADS_TO_START - 1)).map {
|
val electionLogs = (0..(PARALLEL_THREADS_TO_START - 1)).map {
|
||||||
val logContents = logFiles[it]?.readLines()
|
val logContents = logFiles[it]?.readLines()
|
||||||
assertEquals("Compilation on thread $it failed:\n${outStreams[it]}\n\n------- daemon log: -------\n${logContents?.joinToString("\n")}\n-------", 0, resultCodes[it])
|
|
||||||
logContents?.find { it.contains(LOG_PREFIX_ASSUMING_OTHER_DAEMONS_HAVE) }
|
logContents?.find { it.contains(LOG_PREFIX_ASSUMING_OTHER_DAEMONS_HAVE) }
|
||||||
}
|
}
|
||||||
|
|
||||||
assertTrue("No daemon elected: \n${electionLogs.joinToString("\n")}",
|
assertTrue("No daemon elected: \n${electionLogs.joinToString("\n")}",
|
||||||
electionLogs.any { it != null && (it.contains("lower prio") || it.contains("equal prio")) })
|
electionLogs.any { it != null && (it.contains("lower prio") || it.contains("equal prio")) })
|
||||||
|
|
||||||
logFiles.forEach { it?.delete() }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun testDaemonConnectionProblems() {
|
fun testDaemonConnectionProblems() {
|
||||||
withFlagFile(getTestName(true), ".alive") { flagFile ->
|
withFlagFile(getTestName(true), ".alive") { flagFile ->
|
||||||
val daemonOptions = DaemonOptions(shutdownDelayMilliseconds = 1, runFilesPath = File(tmpdir, getTestName(true)).absolutePath)
|
val daemonOptions = makeTestDaemonOptions(getTestName(true))
|
||||||
KotlinCompilerClient.shutdownCompileService(compilerId, daemonOptions)
|
KotlinCompilerClient.shutdownCompileService(compilerId, daemonOptions)
|
||||||
|
|
||||||
val daemonJVMOptions = configureDaemonJVMOptions(inheritMemoryLimits = false, inheritAdditionalProperties = false)
|
val daemonJVMOptions = makeTestDaemonJvmOptions()
|
||||||
val daemon = KotlinCompilerClient.connectToCompileService(compilerId, flagFile, daemonJVMOptions, daemonOptions, DaemonReportingTargets(out = System.err), autostart = true)
|
val daemon = KotlinCompilerClient.connectToCompileService(compilerId, flagFile, daemonJVMOptions, daemonOptions, DaemonReportingTargets(out = System.err), autostart = true)
|
||||||
assertNotNull("failed to connect daemon", daemon)
|
assertNotNull("failed to connect daemon", daemon)
|
||||||
daemon?.registerClient(flagFile.absolutePath)
|
daemon?.registerClient(flagFile.absolutePath)
|
||||||
@@ -500,13 +507,11 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
|
|||||||
|
|
||||||
fun testDaemonCallbackConnectionProblems() {
|
fun testDaemonCallbackConnectionProblems() {
|
||||||
withFlagFile(getTestName(true), ".alive") { flagFile ->
|
withFlagFile(getTestName(true), ".alive") { flagFile ->
|
||||||
val daemonOptions = DaemonOptions(shutdownDelayMilliseconds = 1, runFilesPath = File(tmpdir, getTestName(true)).absolutePath)
|
val daemonOptions = makeTestDaemonOptions(getTestName(true))
|
||||||
KotlinCompilerClient.shutdownCompileService(compilerId, daemonOptions)
|
KotlinCompilerClient.shutdownCompileService(compilerId, daemonOptions)
|
||||||
|
|
||||||
val logFile = createTempFile("kotlin-daemon-test", ".log")
|
withLogFile("kotlin-daemon-test") { logFile ->
|
||||||
val daemonJVMOptions =
|
val daemonJVMOptions = makeTestDaemonJvmOptions(logFile)
|
||||||
configureDaemonJVMOptions("D$COMPILE_DAEMON_LOG_PATH_PROPERTY=\"${logFile.loggerCompatiblePath}\"",
|
|
||||||
inheritMemoryLimits = false, inheritAdditionalProperties = false)
|
|
||||||
|
|
||||||
val daemon = KotlinCompilerClient.connectToCompileService(compilerId, flagFile, daemonJVMOptions, daemonOptions, DaemonReportingTargets(out = System.err), autostart = true)
|
val daemon = KotlinCompilerClient.connectToCompileService(compilerId, flagFile, daemonJVMOptions, daemonOptions, DaemonReportingTargets(out = System.err), autostart = true)
|
||||||
assertNotNull("failed to connect daemon", daemon)
|
assertNotNull("failed to connect daemon", daemon)
|
||||||
@@ -541,7 +546,7 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
|
|||||||
assertTrue("Expecting cancellation message in:\n$compilerOutput", compilerOutput.contains("Compilation was canceled"))
|
assertTrue("Expecting cancellation message in:\n$compilerOutput", compilerOutput.contains("Compilation was canceled"))
|
||||||
logFile.assertLogContainsSequence("error communicating with host, assuming compilation canceled")
|
logFile.assertLogContainsSequence("error communicating with host, assuming compilation canceled")
|
||||||
|
|
||||||
logFile.delete()
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -608,10 +613,8 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
|
|||||||
val daemonOptions = DaemonOptions(autoshutdownIdleSeconds = 1, autoshutdownUnusedSeconds = 1, shutdownDelayMilliseconds = 1, runFilesPath = File(tmpdir, getTestName(true)).absolutePath)
|
val daemonOptions = DaemonOptions(autoshutdownIdleSeconds = 1, autoshutdownUnusedSeconds = 1, shutdownDelayMilliseconds = 1, runFilesPath = File(tmpdir, getTestName(true)).absolutePath)
|
||||||
KotlinCompilerClient.shutdownCompileService(compilerId, daemonOptions)
|
KotlinCompilerClient.shutdownCompileService(compilerId, daemonOptions)
|
||||||
|
|
||||||
val logFile = createTempFile("kotlin-daemon-test", ".log")
|
withLogFile("kotlin-daemon-test") { logFile ->
|
||||||
val daemonJVMOptions =
|
val daemonJVMOptions = makeTestDaemonJvmOptions(logFile)
|
||||||
configureDaemonJVMOptions("D$COMPILE_DAEMON_LOG_PATH_PROPERTY=\"${logFile.loggerCompatiblePath}\"",
|
|
||||||
inheritMemoryLimits = false, inheritAdditionalProperties = false)
|
|
||||||
|
|
||||||
val daemon = KotlinCompilerClient.connectToCompileService(compilerId, flagFile, daemonJVMOptions, daemonOptions, DaemonReportingTargets(out = System.err), autostart = true)
|
val daemon = KotlinCompilerClient.connectToCompileService(compilerId, flagFile, daemonJVMOptions, daemonOptions, DaemonReportingTargets(out = System.err), autostart = true)
|
||||||
assertNotNull("failed to connect daemon", daemon)
|
assertNotNull("failed to connect daemon", daemon)
|
||||||
@@ -643,20 +646,22 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
|
|||||||
Thread.sleep(200)
|
Thread.sleep(200)
|
||||||
logFile.assertLogContainsSequence("Idle timeout exceeded 1s",
|
logFile.assertLogContainsSequence("Idle timeout exceeded 1s",
|
||||||
"Shutdown started")
|
"Shutdown started")
|
||||||
logFile.delete()
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun withDaemon(body: (CompileService) -> Unit) {
|
internal fun withDaemon(body: (CompileService) -> Unit) {
|
||||||
withFlagFile(getTestName(true), ".alive") { flagFile ->
|
withFlagFile(getTestName(true), ".alive") { flagFile ->
|
||||||
val daemonOptions = DaemonOptions(shutdownDelayMilliseconds = 1, runFilesPath = File(tmpdir, getTestName(true)).absolutePath)
|
val daemonOptions = makeTestDaemonOptions(getTestName(true))
|
||||||
val daemonJVMOptions = configureDaemonJVMOptions(inheritMemoryLimits = false, inheritAdditionalProperties = false)
|
withLogFile("kotlin-daemon-test") { logFile ->
|
||||||
|
val daemonJVMOptions = makeTestDaemonJvmOptions(logFile)
|
||||||
val daemon: CompileService? = KotlinCompilerClient.connectToCompileService(compilerId, flagFile, daemonJVMOptions, daemonOptions, DaemonReportingTargets(out = System.err), autostart = true)
|
val daemon: CompileService? = KotlinCompilerClient.connectToCompileService(compilerId, flagFile, daemonJVMOptions, daemonOptions, DaemonReportingTargets(out = System.err), autostart = true)
|
||||||
assertNotNull("failed to connect daemon", daemon)
|
assertNotNull("failed to connect daemon", daemon)
|
||||||
|
|
||||||
body(daemon!!)
|
body(daemon!!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -727,6 +732,17 @@ internal inline fun withFlagFile(prefix: String, suffix: String? = null, body: (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal inline fun withLogFile(prefix: String, suffix: String = ".log", body: (File) -> Unit) {
|
||||||
|
val logFile = createTempFile(prefix, suffix)
|
||||||
|
try {
|
||||||
|
body(logFile)
|
||||||
|
}
|
||||||
|
catch (e: Exception) {
|
||||||
|
System.err.println("--- log file ${logFile.name}:\n${logFile.readText()}\n---")
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// java.util.Logger used in the daemon silently forgets to log into a file specified in the config on Windows,
|
// java.util.Logger used in the daemon silently forgets to log into a file specified in the config on Windows,
|
||||||
// if file path is given in windows form (using backslash as a separator); the reason is unknown
|
// if file path is given in windows form (using backslash as a separator); the reason is unknown
|
||||||
// this function makes a path with forward slashed, that works on windows too
|
// this function makes a path with forward slashed, that works on windows too
|
||||||
|
|||||||
Reference in New Issue
Block a user