Refactor daemon parallel start test to improve diagnostics
also add daemon info to the RPC and remove redundant daemon stopping calls, that should speed up tests execution
This commit is contained in:
@@ -77,6 +77,9 @@ interface CompileService : Remote {
|
||||
@Throws(RemoteException::class)
|
||||
fun getDaemonOptions(): CallResult<DaemonOptions>
|
||||
|
||||
@Throws(RemoteException::class)
|
||||
fun getDaemonInfo(): CallResult<String>
|
||||
|
||||
@Throws(RemoteException::class)
|
||||
fun getDaemonJVMOptions(): CallResult<DaemonJVMOptions>
|
||||
|
||||
|
||||
@@ -206,6 +206,10 @@ class CompileServiceImpl(
|
||||
|
||||
// RMI-exposed API
|
||||
|
||||
override fun getDaemonInfo(): CompileService.CallResult<String> = ifAlive(minAliveness = Aliveness.Dying) {
|
||||
CompileService.CallResult.Good("Kotlin daemon on port $port")
|
||||
}
|
||||
|
||||
override fun getDaemonOptions(): CompileService.CallResult<DaemonOptions> = ifAlive {
|
||||
CompileService.CallResult.Good(daemonOptions)
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import java.io.PrintStream
|
||||
import java.lang.management.ManagementFactory
|
||||
import java.net.URL
|
||||
import java.net.URLClassLoader
|
||||
import java.nio.charset.Charset
|
||||
import java.rmi.server.UnicastRemoteObject
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import java.util.concurrent.Future
|
||||
@@ -105,8 +106,6 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
|
||||
withFlagFile(getTestName(true), ".alive") { flagFile ->
|
||||
val daemonOptions = makeTestDaemonOptions(getTestName(true))
|
||||
|
||||
KotlinCompilerClient.shutdownCompileService(compilerId, daemonOptions)
|
||||
|
||||
withLogFile("kotlin-daemon-test") { logFile ->
|
||||
val daemonJVMOptions = makeTestDaemonJvmOptions(logFile)
|
||||
var daemonShotDown = false
|
||||
@@ -190,9 +189,6 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
|
||||
val compilerId2 = CompilerId.makeCompilerId(compilerClassPath +
|
||||
File(KotlinIntegrationTestBase.getCompilerLib(), "kotlin-compiler-sources.jar"))
|
||||
|
||||
KotlinCompilerClient.shutdownCompileService(compilerId, daemonOptions)
|
||||
KotlinCompilerClient.shutdownCompileService(compilerId2, daemonOptions)
|
||||
|
||||
withLogFile("kotlin-daemon1-test") { logFile1 ->
|
||||
withLogFile("kotlin-daemon2-test") { logFile2 ->
|
||||
val daemonJVMOptions1 = makeTestDaemonJvmOptions(logFile1)
|
||||
@@ -229,8 +225,6 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
|
||||
withFlagFile(getTestName(true), ".alive") { flagFile ->
|
||||
val daemonOptions = DaemonOptions(shutdownDelayMilliseconds = 1, verbose = true, runFilesPath = File(tmpdir, getTestName(true)).absolutePath)
|
||||
|
||||
KotlinCompilerClient.shutdownCompileService(compilerId, daemonOptions)
|
||||
|
||||
val daemonJVMOptions = configureDaemonJVMOptions("-abracadabra", inheritMemoryLimits = false, inheritOtherJvmOptions = false, inheritAdditionalProperties = false)
|
||||
|
||||
val messageCollector = TestMessageCollector()
|
||||
@@ -249,8 +243,6 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
|
||||
withFlagFile(getTestName(true), ".alive") { flagFile ->
|
||||
val daemonOptions = DaemonOptions(shutdownDelayMilliseconds = 1, verbose = true, runFilesPath = File(tmpdir, getTestName(true)).absolutePath)
|
||||
|
||||
KotlinCompilerClient.shutdownCompileService(compilerId, daemonOptions)
|
||||
|
||||
val daemonJVMOptions = configureDaemonJVMOptions(inheritMemoryLimits = false, inheritOtherJvmOptions = false, inheritAdditionalProperties = false)
|
||||
|
||||
val messageCollector = TestMessageCollector()
|
||||
@@ -270,7 +262,6 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
|
||||
fun testDaemonAutoshutdownOnUnused() {
|
||||
withFlagFile(getTestName(true), ".alive") { flagFile ->
|
||||
val daemonOptions = DaemonOptions(autoshutdownUnusedSeconds = 1, shutdownDelayMilliseconds = 1, runFilesPath = File(tmpdir, getTestName(true)).absolutePath)
|
||||
KotlinCompilerClient.shutdownCompileService(compilerId, daemonOptions)
|
||||
|
||||
withLogFile("kotlin-daemon-test") { logFile ->
|
||||
val daemonJVMOptions = makeTestDaemonJvmOptions(logFile)
|
||||
@@ -295,7 +286,6 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
|
||||
fun testDaemonAutoshutdownOnIdle() {
|
||||
withFlagFile(getTestName(true), ".alive") { flagFile ->
|
||||
val daemonOptions = DaemonOptions(autoshutdownIdleSeconds = 1, shutdownDelayMilliseconds = 1, runFilesPath = File(tmpdir, getTestName(true)).absolutePath)
|
||||
KotlinCompilerClient.shutdownCompileService(compilerId, daemonOptions)
|
||||
|
||||
withLogFile("kotlin-daemon-test") { logFile ->
|
||||
val daemonJVMOptions = makeTestDaemonJvmOptions(logFile)
|
||||
@@ -328,7 +318,6 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
|
||||
fun testDaemonGracefulShutdown() {
|
||||
withFlagFile(getTestName(true), ".alive") { flagFile ->
|
||||
val daemonOptions = DaemonOptions(autoshutdownIdleSeconds = 1, shutdownDelayMilliseconds = 1, forceShutdownTimeoutMilliseconds = 60000, runFilesPath = File(tmpdir, getTestName(true)).absolutePath)
|
||||
KotlinCompilerClient.shutdownCompileService(compilerId, daemonOptions)
|
||||
|
||||
withLogFile("kotlin-daemon-test") { logFile ->
|
||||
val daemonJVMOptions = makeTestDaemonJvmOptions(logFile)
|
||||
@@ -386,7 +375,6 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
|
||||
File(getHelloAppBaseDir(), "hello.kt").absolutePath +
|
||||
"-d" + jar
|
||||
try {
|
||||
KotlinCompilerClient.shutdownCompileService(compilerId, daemonOptions)
|
||||
var resOutput: String? = null
|
||||
var resCode: Int? = null
|
||||
// running intermediate process (daemon command line controller) that executes the daemon
|
||||
@@ -408,17 +396,6 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
|
||||
}
|
||||
}
|
||||
|
||||
private class SynchronizationTracer(val startSignal: CountDownLatch, val doneSignal: CountDownLatch, port: Int) : RemoteOperationsTracer,
|
||||
java.rmi.server.UnicastRemoteObject(port, LoopbackNetworkInterface.clientLoopbackSocketFactory, LoopbackNetworkInterface.serverLoopbackSocketFactory)
|
||||
{
|
||||
override fun before(id: String) {
|
||||
startSignal.await()
|
||||
}
|
||||
override fun after(id: String) {
|
||||
doneSignal.countDown()
|
||||
}
|
||||
}
|
||||
|
||||
private val PARALLEL_THREADS_TO_COMPILE = 10
|
||||
private val PARALLEL_WAIT_TIMEOUT_S = 60L
|
||||
|
||||
@@ -477,20 +454,22 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
|
||||
val resultCodes = arrayOfNulls<Int>(PARALLEL_THREADS_TO_START)
|
||||
val outStreams = Array(PARALLEL_THREADS_TO_START, { ByteArrayOutputStream() })
|
||||
val logFiles = arrayOfNulls<File>(PARALLEL_THREADS_TO_START)
|
||||
val daemonInfos = arrayOfNulls<Pair<CompileService.CallResult<String>?, Int?>>(PARALLEL_THREADS_TO_START)
|
||||
|
||||
val daemonOptions = makeTestDaemonOptions(getTestName(true), 100)
|
||||
KotlinCompilerClient.shutdownCompileService(compilerId, daemonOptions)
|
||||
|
||||
fun connectThread(threadNo: Int) = thread(name = "daemonConnect$threadNo") {
|
||||
try {
|
||||
withFlagFile(getTestName(true), ".alive") { flagFile ->
|
||||
withLogFile("kotlin-daemon-test") { logFile ->
|
||||
withLogFile("kotlin-daemon-test", printLogOnException = false) { logFile ->
|
||||
logFiles[threadNo] = logFile
|
||||
val daemonJVMOptions = makeTestDaemonJvmOptions(logFile)
|
||||
val compileServiceSession =
|
||||
KotlinCompilerClient.connectAndLease(compilerId, flagFile, daemonJVMOptions, daemonOptions,
|
||||
DaemonReportingTargets(out = System.err), autostart = true,
|
||||
DaemonReportingTargets(out = PrintStream(outStreams[threadNo])), autostart = true,
|
||||
leaseSession = true)
|
||||
assertNotNull("failed to connect daemon", compileServiceSession?.compileService)
|
||||
daemonInfos[threadNo] = compileServiceSession?.compileService?.getDaemonInfo() to compileServiceSession?.sessionId
|
||||
val jar = tmpdir.absolutePath + File.separator + "hello.$threadNo.jar"
|
||||
val res = KotlinCompilerClient.compile(
|
||||
compileServiceSession!!.compileService,
|
||||
@@ -524,20 +503,49 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
|
||||
|
||||
Thread.sleep(100) // Wait for processes to finish and close log files
|
||||
|
||||
val electionLogs = (0..(PARALLEL_THREADS_TO_START - 1)).map {
|
||||
val logContents = logFiles[it]?.readLines()
|
||||
logContents?.find { it.contains(LOG_PREFIX_ASSUMING_OTHER_DAEMONS_HAVE) }
|
||||
val electionLogs = arrayOfNulls<String>(PARALLEL_THREADS_TO_START)
|
||||
val port2logs = arrayOfNulls<Pair<Int?, File?>>(PARALLEL_THREADS_TO_START)
|
||||
|
||||
for (i in 0..(PARALLEL_THREADS_TO_START - 1)) {
|
||||
val logContents = logFiles[i]?.readLines()
|
||||
port2logs[i] = logContents?.find { it.contains("daemon is listening on port") }?.split(" ")?.last()?.toIntOrNull() to logFiles[i]
|
||||
electionLogs[i] = logContents?.find { it.contains(LOG_PREFIX_ASSUMING_OTHER_DAEMONS_HAVE) }
|
||||
}
|
||||
|
||||
assertTrue("No daemon elected: \n${electionLogs.joinToString("\n")}",
|
||||
electionLogs.any { it != null && (it.contains("lower prio") || it.contains("equal prio")) })
|
||||
val electionsSuccess = electionLogs.any { it != null && (it.contains("lower prio") || it.contains("equal prio")) }
|
||||
val resultsSuccess = resultCodes.all { it == 0 }
|
||||
|
||||
if (!electionsSuccess || !resultsSuccess) {
|
||||
val msg = buildString {
|
||||
for (i in 0..PARALLEL_THREADS_TO_START - 1) {
|
||||
val daemonInfoRes = daemonInfos[i]?.first
|
||||
var compiledPort: Int? = null
|
||||
val daemonInfo = when (daemonInfoRes) {
|
||||
is CompileService.CallResult.Good -> daemonInfoRes.get().also {
|
||||
compiledPort = split(" ").last().toIntOrNull()
|
||||
}
|
||||
is CompileService.CallResult.Dying -> "<dying>"
|
||||
is CompileService.CallResult.Error -> "<error: ${daemonInfoRes.message}>"
|
||||
else -> "?"
|
||||
}
|
||||
appendln("#$i\tcompiled on $daemonInfo, session ${daemonInfos[i]?.second}, result ${resultCodes[i]}; started daemon on port ${port2logs[i]?.first}, log: ${logFiles[i]?.canonicalPath}")
|
||||
if (resultCodes[i] != 0 || electionLogs[i] == null) {
|
||||
appendln("--- out $i, result ${resultCodes[i]}:\n${outStreams[i].toByteArray().toString(Charset.defaultCharset())}\n---")
|
||||
compiledPort?.let { port -> port2logs.find { it?.first == port } }?.second?.let { logFile ->
|
||||
appendln("--- log file ${logFile.name}:\n${logFile.readText()}\n---")
|
||||
}
|
||||
?: appendln("--- log not found")
|
||||
}
|
||||
}
|
||||
}
|
||||
assertTrue("No daemon elected:\n\n$msg\n--- elections:\n${electionLogs.joinToString("\n")}\n---", electionsSuccess)
|
||||
assertTrue("Some compilations failed:\n\nmsg", resultsSuccess)
|
||||
}
|
||||
}
|
||||
|
||||
fun testDaemonConnectionProblems() {
|
||||
withFlagFile(getTestName(true), ".alive") { flagFile ->
|
||||
val daemonOptions = makeTestDaemonOptions(getTestName(true))
|
||||
KotlinCompilerClient.shutdownCompileService(compilerId, daemonOptions)
|
||||
|
||||
val daemonJVMOptions = makeTestDaemonJvmOptions()
|
||||
val daemon = KotlinCompilerClient.connectToCompileService(compilerId, flagFile, daemonJVMOptions, daemonOptions, DaemonReportingTargets(out = System.err), autostart = true)
|
||||
assertNotNull("failed to connect daemon", daemon)
|
||||
@@ -567,7 +575,6 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
|
||||
fun testDaemonCallbackConnectionProblems() {
|
||||
withFlagFile(getTestName(true), ".alive") { flagFile ->
|
||||
val daemonOptions = makeTestDaemonOptions(getTestName(true))
|
||||
KotlinCompilerClient.shutdownCompileService(compilerId, daemonOptions)
|
||||
|
||||
withLogFile("kotlin-daemon-test") { logFile ->
|
||||
val daemonJVMOptions = makeTestDaemonJvmOptions(logFile)
|
||||
@@ -670,7 +677,6 @@ class CompilerDaemonTest : KotlinIntegrationTestBase() {
|
||||
fun testDaemonReplAutoshutdownOnIdle() {
|
||||
withFlagFile(getTestName(true), ".alive") { flagFile ->
|
||||
val daemonOptions = DaemonOptions(autoshutdownIdleSeconds = 1, autoshutdownUnusedSeconds = 1, shutdownDelayMilliseconds = 1, runFilesPath = File(tmpdir, getTestName(true)).absolutePath)
|
||||
KotlinCompilerClient.shutdownCompileService(compilerId, daemonOptions)
|
||||
|
||||
withLogFile("kotlin-daemon-test") { logFile ->
|
||||
val daemonJVMOptions = makeTestDaemonJvmOptions(logFile)
|
||||
@@ -791,13 +797,16 @@ internal inline fun withFlagFile(prefix: String, suffix: String? = null, body: (
|
||||
}
|
||||
}
|
||||
|
||||
internal inline fun withLogFile(prefix: String, suffix: String = ".log", body: (File) -> Unit) {
|
||||
internal inline fun withLogFile(prefix: String, suffix: String = ".log", printLogOnException: Boolean = true, 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---")
|
||||
if (printLogOnException) {
|
||||
Thread.sleep(100) // trying to wait log flushing
|
||||
System.err.println("--- log file ${logFile.name}:\n${logFile.readText()}\n---")
|
||||
}
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user