[split tests] AT-3386 Start different IDE instance depending on the ide mode: split, monolith

GitOrigin-RevId: 896372420c21ed899da488affbfa71f4630bc8ef
This commit is contained in:
Nikita Kudrin
2025-10-30 09:37:01 +00:00
committed by intellij-monorepo-bot
parent db00ad5d0c
commit a5d46b92c7
4 changed files with 74 additions and 42 deletions
@@ -1,13 +1,15 @@
package com.intellij.lambda.testFramework.junit
import com.intellij.ide.starter.junit5.RemoteDevRun
import com.intellij.remoteDev.tests.modelGenerated.LambdaRdIdeType
import com.intellij.remoteDev.tests.modelGenerated.LambdaRdIdeType.*
import com.intellij.testFramework.junit5.impl.TestApplicationExtension
import org.junit.jupiter.api.TestInstance
import org.junit.jupiter.api.extension.ExtendWith
import java.lang.annotation.Inherited
enum class IdeRunMode {
MONOLITH, SPLIT
}
/**
* Test will be executed in monolith and in split mode.
*/
@@ -20,4 +22,4 @@ import java.lang.annotation.Inherited
MonolithAndSplitModeIdeInstanceInitializer::class,
RemoteDevRun::class)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
annotation class ExecuteInMonolithAndSplitMode(vararg val mode: LambdaRdIdeType = [FRONTEND, BACKEND, MONOLITH])
annotation class ExecuteInMonolithAndSplitMode(vararg val mode: IdeRunMode = [IdeRunMode.MONOLITH, IdeRunMode.SPLIT])
@@ -2,7 +2,6 @@ package com.intellij.lambda.testFramework.junit
import com.intellij.lambda.testFramework.utils.BackgroundRunWithLambda
import com.intellij.openapi.diagnostic.currentClassLogger
import com.intellij.remoteDev.tests.modelGenerated.LambdaRdIdeType
import org.junit.jupiter.api.extension.*
import org.junit.jupiter.params.provider.Arguments
import org.junit.jupiter.params.provider.ArgumentsProvider
@@ -16,7 +15,7 @@ import java.util.stream.Stream
class MonolithAndSplitModeContextProvider : TestTemplateInvocationContextProvider {
override fun supportsTestTemplate(context: ExtensionContext): Boolean = getModesToRun(context).isNotEmpty()
private fun getModesToRun(context: ExtensionContext): List<LambdaRdIdeType> {
private fun getModesToRun(context: ExtensionContext): List<IdeRunMode> {
val methodAnnotation = AnnotationUtils.findAnnotation(context.testMethod, ExecuteInMonolithAndSplitMode::class.java)
if (methodAnnotation.isPresent) {
return methodAnnotation.get().mode.toList()
@@ -41,7 +40,7 @@ class MonolithAndSplitModeContextProvider : TestTemplateInvocationContextProvide
try {
// PARAMETERIZED CASE: Generate tests for each argument in each LambdaRdIdeType
provider.provideArguments(context).flatMap { args: Arguments ->
modesToRun.stream().map { mode -> createInvocationContext(mode, args.get()) }
modesToRun.stream().map { mode -> createInvocationContext(mode, args.get(), context) }
}
}
catch (e: Exception) {
@@ -51,12 +50,14 @@ class MonolithAndSplitModeContextProvider : TestTemplateInvocationContextProvide
if (parametrizedRuns.isPresent) return parametrizedRuns.get()
// SIMPLE CASE - test without parameters: Generate one test for each LambdaRdIdeType
return modesToRun.stream().map { mode -> createInvocationContext(mode, emptyArray()) }
return modesToRun.stream().map { mode -> createInvocationContext(mode, emptyArray(), context) }
}
private fun createInvocationContext(mode: LambdaRdIdeType, args: Array<Any>): TestTemplateInvocationContext {
private fun createInvocationContext(mode: IdeRunMode, args: Array<Any>, context: ExtensionContext): TestTemplateInvocationContext {
return object : TestTemplateInvocationContext {
override fun getDisplayName(invocationIndex: Int): String {
MonolithAndSplitModeIdeInstanceInitializer.startIde(mode, context)
val params = if (args.isNotEmpty()) " with params: " + listOf(*args) else ""
return if (params.isNotEmpty()) "[$mode] $params" else "[$mode]"
}
@@ -67,18 +68,17 @@ class MonolithAndSplitModeContextProvider : TestTemplateInvocationContextProvide
val type = paramCtx.parameter.type
return type.isAssignableFrom(BackgroundRunWithLambda::class.java) ||
type.isAssignableFrom(mode::class.java) ||
(paramCtx.index > 0 && paramCtx.index - 1 < args.size)
(paramCtx.index > 0 && paramCtx.index - 1 < args.size)
}
override fun resolveParameter(paramCtx: ParameterContext, extCtx: ExtensionContext): Any {
return when {
paramCtx.parameter.type.isAssignableFrom(mode::class.java) -> mode
paramCtx.parameter.type.isAssignableFrom(BackgroundRunWithLambda::class.java) -> {
// Assuming BackgroundRunWithLambda requires specific instantiation logic
MonolithAndSplitModeIdeInstanceInitializer.ideBackgroundRun
MonolithAndSplitModeIdeInstanceInitializer.startIde(mode, context)
}
else -> {
// The first parameter is the LambdaRdIdeType, so offset argument index by 1
// The first parameter is the IdeRunMode, so offset argument index by 1
args[paramCtx.index - 1]
}
}
@@ -1,49 +1,57 @@
package com.intellij.lambda.testFramework.junit
import com.intellij.ide.starter.ide.IdeProductProvider
import com.intellij.ide.starter.models.TestCase
import com.intellij.ide.starter.project.LocalProjectInfo
import com.intellij.ide.starter.project.TestCaseTemplate
import com.intellij.ide.starter.config.ConfigurationStorage
import com.intellij.ide.starter.config.splitMode
import com.intellij.ide.starter.runner.Starter
import com.intellij.ide.starter.utils.catchAll
import com.intellij.lambda.testFramework.starter.newContextWithLambda
import com.intellij.lambda.testFramework.utils.BackgroundRunWithLambda
import com.intellij.lambda.testFramework.utils.IdeLambdaStarter
import com.intellij.lambda.testFramework.utils.IdeLambdaStarter.runIdeWithLambda
import com.intellij.openapi.application.PathManager
import org.junit.jupiter.api.extension.AfterAllCallback
import org.junit.jupiter.api.extension.BeforeAllCallback
import org.junit.jupiter.api.extension.ExtensionContext
import java.nio.file.Paths
import kotlin.io.path.createDirectories
class MonolithAndSplitModeIdeInstanceInitializer : BeforeAllCallback, AfterAllCallback {
class MonolithAndSplitModeIdeInstanceInitializer : AfterAllCallback {
companion object {
lateinit var ideBackgroundRun: BackgroundRunWithLambda
}
private set
lateinit var currentIdeMode: IdeRunMode
private set
fun startIde(runMode: IdeRunMode, context: ExtensionContext): BackgroundRunWithLambda {
synchronized(this) {
if (this::ideBackgroundRun.isInitialized && currentIdeMode == runMode) {
println("IDE is already running in mode: $runMode. Reusing the current instance of IDE.")
return ideBackgroundRun
}
stopIde()
currentIdeMode = runMode
ConfigurationStorage.splitMode(currentIdeMode == IdeRunMode.SPLIT)
println("Starting IDE in mode: $runMode")
val testContext = Starter.newContextWithLambda(context.testClass.get().simpleName,
UltimateTestCases.JpsEmptyProject,
additionalPluginModule = IdeLambdaStarter.ADDITIONAL_LAMBDA_TEST_PLUGIN)
ideBackgroundRun = testContext.runIdeWithLambda()
return ideBackgroundRun
}
}
private fun stopIde() {
synchronized(this) {
if (!this::ideBackgroundRun.isInitialized) return
println("Stopping IDE that is running in mode: $currentIdeMode")
catchAll { ideBackgroundRun.closeIdeAndWait() }
}
}
override fun beforeAll(context: ExtensionContext) {
val testContext = Starter.newContextWithLambda(context.testClass.get().simpleName,
UltimateTestCases.JpsEmptyProject,
additionalPluginModule = IdeLambdaStarter.ADDITIONAL_LAMBDA_TEST_PLUGIN)
ideBackgroundRun = testContext.runIdeWithLambda()
}
override fun afterAll(context: ExtensionContext) {
catchAll { ideBackgroundRun.closeIdeAndWait() }
stopIde()
}
}
object UltimateTestCases : TestCaseTemplate(IdeProductProvider.IU) {
val JpsEmptyProject: TestCase<LocalProjectInfo> = withProject(
projectInfo = LocalProjectInfo(
projectDir = Paths.get(PathManager.getHomePath(), "out/ide-tests/cache/empty-project").createDirectories()
)
)
val JavaTestProject: TestCase<LocalProjectInfo> = withProject(
projectInfo = LocalProjectInfo(
projectDir = Paths.get(PathManager.getHomePath(), "out/ide-tests/cache/projects/unpacked/java-ui-automation-data").createDirectories()
)
)
}
@@ -0,0 +1,22 @@
package com.intellij.lambda.testFramework.junit
import com.intellij.ide.starter.ide.IdeProductProvider
import com.intellij.ide.starter.models.TestCase
import com.intellij.ide.starter.project.LocalProjectInfo
import com.intellij.ide.starter.project.TestCaseTemplate
import com.intellij.openapi.application.PathManager
import java.nio.file.Paths
import kotlin.io.path.createDirectories
object UltimateTestCases : TestCaseTemplate(IdeProductProvider.IU) {
val JpsEmptyProject: TestCase<LocalProjectInfo> = withProject(
projectInfo = LocalProjectInfo(
projectDir = Paths.get(PathManager.getHomePath(), "out/ide-tests/cache/empty-project").createDirectories()
)
)
val JavaTestProject: TestCase<LocalProjectInfo> = withProject(
projectInfo = LocalProjectInfo(
projectDir = Paths.get(PathManager.getHomePath(), "out/ide-tests/cache/projects/unpacked/java-ui-automation-data").createDirectories()
)
)
}