diff --git a/compiler/daemon/daemon-tests/test/org/jetbrains/kotlin/daemon/BaseDaemonSessionTest.kt b/compiler/daemon/daemon-tests/test/org/jetbrains/kotlin/daemon/BaseDaemonSessionTest.kt new file mode 100644 index 00000000000..323f02b342b --- /dev/null +++ b/compiler/daemon/daemon-tests/test/org/jetbrains/kotlin/daemon/BaseDaemonSessionTest.kt @@ -0,0 +1,108 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.daemon + +import org.jetbrains.kotlin.cli.common.CompilerSystemProperties +import org.jetbrains.kotlin.daemon.client.CompileServiceSession +import org.jetbrains.kotlin.daemon.client.DaemonReportingTargets +import org.jetbrains.kotlin.daemon.client.KotlinCompilerClient +import org.jetbrains.kotlin.daemon.common.CompileService +import org.jetbrains.kotlin.daemon.common.CompilerId +import org.jetbrains.kotlin.daemon.common.DaemonJVMOptions +import org.jetbrains.kotlin.daemon.common.DaemonOptions +import org.jetbrains.kotlin.integration.KotlinIntegrationTestBase +import org.jetbrains.kotlin.test.util.KtTestUtil +import org.jetbrains.kotlin.utils.KotlinPaths +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.io.TempDir +import java.io.File + +/** + * Use this class as the base for tests that lease daemon sessions. + * + * It provides you with the [leaseSession] method that leases a session and registers it, so at the end of the test's execution + * the daemon will be shut down. + * + * [LastSessionDaemonTest] is an example of such a test suit. + */ +abstract class BaseDaemonSessionTest { + @TempDir + lateinit var workingDirectory: File + + private val compilerClassPath = KotlinIntegrationTestBase.getKotlinPaths().classPath(KotlinPaths.ClassPaths.Compiler) + + private val compilerId by lazy(LazyThreadSafetyMode.NONE) { CompilerId.makeCompilerId(compilerClassPath) } + + private val compileServices = mutableSetOf() + + val outputDirectory + get() = workingDirectory.resolve("output") + + open val defaultDaemonOptions + get() = DaemonOptions( + File(workingDirectory, "daemon-files").absolutePath, + shutdownDelayMilliseconds = 0, + autoshutdownUnusedSeconds = 1, + autoshutdownIdleSeconds = 1, + ) + + open val defaultDaemonJvmOptions + get() = DaemonJVMOptions( + maxMemory = "384m" + ) + + @AfterEach + fun stopDaemons() { + for (compileService in compileServices) { + runCatching { compileService.shutdown() } + } + Thread.sleep(500) // wait a bit so that all the daemons are shut down + } + + fun getHelloAppBaseDir(): String = KtTestUtil.getTestDataPathBase() + "/integration/smoke/helloApp" + + private fun DaemonJVMOptions.withLogFile(logFile: File) = copy( + jvmParams = (jvmParams + "D${CompilerSystemProperties.COMPILE_DAEMON_LOG_PATH_PROPERTY.property}=\"${logFile.loggerCompatiblePath}\"").toMutableList() + ) + + fun leaseSession( + clientMarkerFile: File = workingDirectory.resolve("client.alive"), + sessionMarkerFile: File = workingDirectory.resolve("session.alive"), + jvmOptions: DaemonJVMOptions = defaultDaemonJvmOptions, + daemonOptions: DaemonOptions = defaultDaemonOptions, + logFile: File? = null, + ): CompileServiceSession { + val actualJvmOptions = logFile?.let { jvmOptions.withLogFile(it) } ?: jvmOptions + println("Leasing a session with $actualJvmOptions and $daemonOptions") + clientMarkerFile.createNewFile() + sessionMarkerFile.createNewFile() + logFile?.createNewFile() + return KotlinCompilerClient.connectAndLease( + compilerId, + clientMarkerFile, + actualJvmOptions, + daemonOptions, + DaemonReportingTargets(out = System.err), + autostart = true, + leaseSession = true, + sessionAliveFlagFile = sessionMarkerFile, + )?.also { compileServices.add(it.compileService) } ?: error("failed to connect daemon") + } +} + +fun File.assertLogFileContains(vararg substrings: String) { + val text = readText() + val notFound = substrings.filterNot { it in text } + assert(notFound.isEmpty()) { + """ + |$this does not contain the following substrings: + |${notFound.joinToString(System.lineSeparator())} + | + |The whole file content: + |$text + """.trimMargin() + } +} \ No newline at end of file diff --git a/compiler/daemon/daemon-tests/test/org/jetbrains/kotlin/daemon/LastSessionDaemonTest.kt b/compiler/daemon/daemon-tests/test/org/jetbrains/kotlin/daemon/LastSessionDaemonTest.kt new file mode 100644 index 00000000000..2e87265febb --- /dev/null +++ b/compiler/daemon/daemon-tests/test/org/jetbrains/kotlin/daemon/LastSessionDaemonTest.kt @@ -0,0 +1,49 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.daemon + +import org.jetbrains.kotlin.daemon.client.KotlinCompilerClient +import org.jetbrains.kotlin.daemon.common.CompileService +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import java.io.File +import java.lang.Thread.sleep +import kotlin.test.assertEquals + +@DisplayName("Compiler daemon operations test in the LastSession mode") +class LastSessionDaemonTest : BaseDaemonSessionTest() { + private val logFile + get() = workingDirectory.resolve("daemon.log") + + @DisplayName("Already leased session can perform compilation") + @Test + fun canCompileInLastSessionMode() { + val (compileService, sessionId) = leaseSession(logFile = logFile) + sleep(DAEMON_PERIODIC_CHECK_INTERVAL_MS + 1_000) + logFile.assertLogFileContains("Some sessions are active, waiting for them to finish") + val testMessageCollector = TestMessageCollector() + val exitCode = KotlinCompilerClient.compile( + compileService, + sessionId, + CompileService.TargetPlatform.JVM, + arrayOf("-include-runtime", File(getHelloAppBaseDir(), "hello.kt").absolutePath, "-d", outputDirectory.absolutePath), + testMessageCollector + ) + assertEquals(0, exitCode) + } + + @Disabled("KT-55322") + @DisplayName("can lease a session when the daemon in the LastSession state") // either by starting a new daemon or returning it to the Alive state + @Test + fun canLeaseNewSession() { + leaseSession(logFile = logFile) + sleep(DAEMON_PERIODIC_CHECK_INTERVAL_MS + 1_000) + logFile.assertLogFileContains("Some sessions are active, waiting for them to finish") + // trying to lease a session with the same config again + leaseSession(logFile = logFile) + } +} \ No newline at end of file