Some basic daemon lifetime tests
This commit is contained in:
@@ -27,7 +27,6 @@ import java.io.ByteArrayOutputStream
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.concurrent.CountDownLatch
|
import java.util.concurrent.CountDownLatch
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
|
||||||
import kotlin.concurrent.thread
|
import kotlin.concurrent.thread
|
||||||
|
|
||||||
|
|
||||||
@@ -67,8 +66,7 @@ public class CompilerDaemonTest : KotlinIntegrationTestBase() {
|
|||||||
|
|
||||||
|
|
||||||
public fun testHelloApp() {
|
public fun testHelloApp() {
|
||||||
val flagFile = createTempFile(getTestName(true), ".alive")
|
witFlagFile(getTestName(true), ".alive") { flagFile ->
|
||||||
try {
|
|
||||||
val daemonOptions = DaemonOptions(runFilesPath = File(tmpdir, getTestName(true)).absolutePath,
|
val daemonOptions = DaemonOptions(runFilesPath = File(tmpdir, getTestName(true)).absolutePath,
|
||||||
verbose = true,
|
verbose = true,
|
||||||
reportPerf = true)
|
reportPerf = true)
|
||||||
@@ -113,9 +111,6 @@ public class CompilerDaemonTest : KotlinIntegrationTestBase() {
|
|||||||
KotlinCompilerClient.shutdownCompileService(compilerId, daemonOptions)
|
KotlinCompilerClient.shutdownCompileService(compilerId, daemonOptions)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
flagFile.delete()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun testDaemonJvmOptionsParsing() {
|
public fun testDaemonJvmOptionsParsing() {
|
||||||
@@ -146,10 +141,9 @@ public class CompilerDaemonTest : KotlinIntegrationTestBase() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun testDaemonInstances() {
|
public fun testDaemonInstancesSimple() {
|
||||||
val jar = tmpdir.absolutePath + File.separator + "hello1.jar"
|
val jar = tmpdir.absolutePath + File.separator + "hello1.jar"
|
||||||
val flagFile = createTempFile(getTestName(true), ".alive")
|
witFlagFile(getTestName(true), ".alive") { flagFile ->
|
||||||
try {
|
|
||||||
val daemonOptions = DaemonOptions(runFilesPath = File(tmpdir, getTestName(true)).absolutePath)
|
val daemonOptions = DaemonOptions(runFilesPath = File(tmpdir, getTestName(true)).absolutePath)
|
||||||
val compilerId2 = CompilerId.makeCompilerId(compilerClassPath +
|
val compilerId2 = CompilerId.makeCompilerId(compilerClassPath +
|
||||||
File(KotlinIntegrationTestBase.getCompilerLib(), "kotlin-compiler-sources.jar"))
|
File(KotlinIntegrationTestBase.getCompilerLib(), "kotlin-compiler-sources.jar"))
|
||||||
@@ -188,11 +182,102 @@ public class CompilerDaemonTest : KotlinIntegrationTestBase() {
|
|||||||
logFile2.assertLogContainsSequence("Shutdown complete")
|
logFile2.assertLogContainsSequence("Shutdown complete")
|
||||||
logFile2.delete()
|
logFile2.delete()
|
||||||
}
|
}
|
||||||
finally {
|
}
|
||||||
flagFile.delete()
|
|
||||||
|
public fun testDaemonAutoshutdownOnUnused() {
|
||||||
|
witFlagFile(getTestName(true), ".alive") { flagFile ->
|
||||||
|
val daemonOptions = DaemonOptions(autoshutdownUnusedSeconds = 1, runFilesPath = File(tmpdir, getTestName(true)).absolutePath)
|
||||||
|
KotlinCompilerClient.shutdownCompileService(compilerId, daemonOptions)
|
||||||
|
|
||||||
|
val logFile = createTempFile("kotlin-daemon-test", ".log")
|
||||||
|
val daemonJVMOptions =
|
||||||
|
configureDaemonJVMOptions("D$COMPILE_DAEMON_LOG_PATH_PROPERTY=\"${logFile.absolutePath}\"",
|
||||||
|
inheritMemoryLimits = false, inheritAdditionalProperties = false)
|
||||||
|
|
||||||
|
val daemon = KotlinCompilerClient.connectToCompileService(compilerId, flagFile, daemonJVMOptions, daemonOptions, DaemonReportingTargets(out = System.err), autostart = true)
|
||||||
|
TestCase.assertNotNull("failed to connect daemon", daemon)
|
||||||
|
daemon?.registerClient(flagFile.absolutePath)
|
||||||
|
|
||||||
|
// wait up to 4s (more than 1s unused timeout)
|
||||||
|
for (attempts in 1..20) {
|
||||||
|
if (logFile.isLogContainsSequence("Unused timeout exceeded 1s")) break
|
||||||
|
Thread.sleep(200)
|
||||||
|
}
|
||||||
|
Thread.sleep(200)
|
||||||
|
|
||||||
|
logFile.assertLogContainsSequence("Unused timeout exceeded 1s",
|
||||||
|
"Shutdown complete")
|
||||||
|
logFile.delete()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public fun testDaemonAutoshutdownOnIdle() {
|
||||||
|
witFlagFile(getTestName(true), ".alive") { flagFile ->
|
||||||
|
val daemonOptions = DaemonOptions(autoshutdownIdleSeconds = 1, runFilesPath = File(tmpdir, getTestName(true)).absolutePath)
|
||||||
|
KotlinCompilerClient.shutdownCompileService(compilerId, daemonOptions)
|
||||||
|
|
||||||
|
val logFile = createTempFile("kotlin-daemon-test", ".log")
|
||||||
|
val daemonJVMOptions =
|
||||||
|
configureDaemonJVMOptions("D$COMPILE_DAEMON_LOG_PATH_PROPERTY=\"${logFile.absolutePath}\"",
|
||||||
|
inheritMemoryLimits = false, inheritAdditionalProperties = false)
|
||||||
|
|
||||||
|
val daemon = KotlinCompilerClient.connectToCompileService(compilerId, flagFile, daemonJVMOptions, daemonOptions, DaemonReportingTargets(out = System.err), autostart = true)
|
||||||
|
TestCase.assertNotNull("failed to connect daemon", daemon)
|
||||||
|
daemon?.registerClient(flagFile.absolutePath)
|
||||||
|
val strm = ByteArrayOutputStream()
|
||||||
|
val code = KotlinCompilerClient.compile(daemon!!, CompileService.NO_SESSION, CompileService.TargetPlatform.JVM, arrayOf("-include-runtime"), strm)
|
||||||
|
TestCase.assertEquals("compilation failed:\n${strm.toString()}", 0, code)
|
||||||
|
|
||||||
|
logFile.assertLogContainsSequence("Starting compilation with args: ")
|
||||||
|
|
||||||
|
// wait up to 4s (more than 1s idle timeout)
|
||||||
|
for (attempts in 1..20) {
|
||||||
|
if (logFile.isLogContainsSequence("Idle timeout exceeded 1s")) break
|
||||||
|
Thread.sleep(200)
|
||||||
|
}
|
||||||
|
Thread.sleep(200)
|
||||||
|
logFile.assertLogContainsSequence("Idle timeout exceeded 1s",
|
||||||
|
"Shutdown complete")
|
||||||
|
logFile.delete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun testDaemonGracefulShutdown() {
|
||||||
|
witFlagFile(getTestName(true), ".alive") { flagFile ->
|
||||||
|
val daemonOptions = DaemonOptions(autoshutdownIdleSeconds = 1, runFilesPath = File(tmpdir, getTestName(true)).absolutePath)
|
||||||
|
KotlinCompilerClient.shutdownCompileService(compilerId, daemonOptions)
|
||||||
|
|
||||||
|
val logFile = createTempFile("kotlin-daemon-test", ".log")
|
||||||
|
val daemonJVMOptions =
|
||||||
|
configureDaemonJVMOptions("D$COMPILE_DAEMON_LOG_PATH_PROPERTY=\"${logFile.absolutePath}\"",
|
||||||
|
inheritMemoryLimits = false, inheritAdditionalProperties = false)
|
||||||
|
|
||||||
|
val daemon = KotlinCompilerClient.connectToCompileService(compilerId, flagFile, daemonJVMOptions, daemonOptions, DaemonReportingTargets(out = System.err), autostart = true)
|
||||||
|
TestCase.assertNotNull("failed to connect daemon", daemon)
|
||||||
|
daemon?.registerClient(flagFile.absolutePath)
|
||||||
|
val sessionId = daemon?.leaseCompileSession(null)
|
||||||
|
|
||||||
|
val scheduleShutdownRes = daemon?.scheduleShutdown(true)
|
||||||
|
|
||||||
|
TestCase.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
|
||||||
|
|
||||||
|
logFile.assertLogContainsSequence("Some sessions are active, waiting for them to finish")
|
||||||
|
|
||||||
|
val res = daemon?.getUsedMemory()
|
||||||
|
|
||||||
|
TestCase.assertEquals("Invalid state", CompileService.CallResult.Dying(), res)
|
||||||
|
|
||||||
|
daemon?.releaseCompileSession(sessionId!!.get())
|
||||||
|
|
||||||
|
Thread.sleep(100) // allow after session timed action to run
|
||||||
|
|
||||||
|
logFile.assertLogContainsSequence("All sessions finished, shutting down",
|
||||||
|
"Shutdown complete")
|
||||||
|
logFile.delete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Testing that running daemon in the child process doesn't block on s child process.waitFor()
|
/** Testing that running daemon in the child process doesn't block on s child process.waitFor()
|
||||||
* that may happen on windows if simple processBuilder.start is used due to handles inheritance:
|
* that may happen on windows if simple processBuilder.start is used due to handles inheritance:
|
||||||
@@ -260,8 +345,7 @@ public class CompilerDaemonTest : KotlinIntegrationTestBase() {
|
|||||||
|
|
||||||
TestCase.assertTrue(PARALLEL_THREADS_TO_COMPILE <= LoopbackNetworkInterface.SERVER_SOCKET_BACKLOG_SIZE)
|
TestCase.assertTrue(PARALLEL_THREADS_TO_COMPILE <= LoopbackNetworkInterface.SERVER_SOCKET_BACKLOG_SIZE)
|
||||||
|
|
||||||
val flagFile = createTempFile(getTestName(true), ".alive")
|
witFlagFile(getTestName(true), ".alive") { flagFile ->
|
||||||
try {
|
|
||||||
val daemonOptions = DaemonOptions(runFilesPath = File(tmpdir, getTestName(true)).absolutePath)
|
val daemonOptions = DaemonOptions(runFilesPath = File(tmpdir, getTestName(true)).absolutePath)
|
||||||
val daemonJVMOptions = configureDaemonJVMOptions(inheritMemoryLimits = false, inheritAdditionalProperties = false)
|
val daemonJVMOptions = configureDaemonJVMOptions(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)
|
||||||
@@ -302,22 +386,29 @@ public class CompilerDaemonTest : KotlinIntegrationTestBase() {
|
|||||||
TestCase.assertEquals("Compilation on thread $it failed:\n${outStreams[it - 1]}", 0, resultCodes[it - 1])
|
TestCase.assertEquals("Compilation on thread $it failed:\n${outStreams[it - 1]}", 0, resultCodes[it - 1])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally {
|
|
||||||
flagFile.delete()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun File.assertLogContainsSequence(vararg patterns: String) {
|
internal fun File.ifLogNotContainsSequence(vararg patterns: String, body: (LinePattern, Int) -> Unit) {
|
||||||
reader().useLines {
|
reader().useLines {
|
||||||
it.ifNotContainsSequence( patterns.map { LinePattern(it) })
|
it.ifNotContainsSequence( patterns.map { LinePattern(it) }, body)
|
||||||
{
|
|
||||||
pattern,lineNo -> TestCase.fail("Pattern '${pattern.regex}' is not found in the log file '$absolutePath'")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun File.assertLogContainsSequence(vararg patterns: String) {
|
||||||
|
ifLogNotContainsSequence(*patterns)
|
||||||
|
{
|
||||||
|
pattern,lineNo -> TestCase.fail("Pattern '${pattern.regex}' is not found in the log file '$absolutePath'")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun File.isLogContainsSequence(vararg patterns: String): Boolean {
|
||||||
|
var res = true
|
||||||
|
ifLogNotContainsSequence(*patterns) { l,c -> res = false }
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
fun restoreSystemProperty(propertyName: String, backupValue: String?) {
|
fun restoreSystemProperty(propertyName: String, backupValue: String?) {
|
||||||
if (backupValue == null) {
|
if (backupValue == null) {
|
||||||
System.clearProperty(propertyName)
|
System.clearProperty(propertyName)
|
||||||
@@ -326,3 +417,13 @@ fun restoreSystemProperty(propertyName: String, backupValue: String?) {
|
|||||||
System.setProperty(propertyName, backupValue)
|
System.setProperty(propertyName, backupValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal inline fun witFlagFile(prefix: String, suffix: String? = null, body: (File) -> Unit) {
|
||||||
|
val file = createTempFile(prefix, suffix)
|
||||||
|
try {
|
||||||
|
body(file)
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
file.delete()
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user