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:
Ilya Chernikov
2017-04-06 12:56:59 +02:00
parent 7600b6de52
commit bec2ae7cab
3 changed files with 269 additions and 235 deletions
@@ -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)
@@ -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,14 +646,15 @@ 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)
@@ -658,6 +662,7 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
} }
} }
} }
}
// stolen from CompilerFileLimitTest // stolen from CompilerFileLimitTest
@@ -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