testFramework [lambda]: more straightforward way to set scope for processes without major refactoring

GitOrigin-RevId: 3ddb37f11c178767a5121c4f1b0922cf642b2ed4
This commit is contained in:
Anastasia Katsman
2026-02-16 22:04:04 +00:00
committed by intellij-monorepo-bot
parent e5ebd54c9c
commit f378bc7b85
7 changed files with 33 additions and 13 deletions
@@ -1,5 +0,0 @@
junit.jupiter.extensions.autodetection.enabled = true
junit.jupiter.extensions.autodetection.exclude = com.intellij.ide.starter.junit5.config.KillOutdatedProcessesAfterEach,com.intellij.ide.starter.junit5.config.KillOutdatedProcesses
# Will be handled by com.intellij.lambda.testFramework.starter.LambdaTestExecutionListener
junit.platform.execution.listeners.deactivate = com.intellij.ide.starter.junit5.TestCleanupListener
@@ -2,10 +2,13 @@ package com.intellij.lambda.testFramework.starter
import com.intellij.ide.starter.ci.CIServer
import com.intellij.ide.starter.ci.teamcity.TeamCityCIServer
import com.intellij.ide.starter.coroutine.CommonScope
import com.intellij.ide.starter.coroutine.CommonScope.testSuiteSupervisorScope
import com.intellij.ide.starter.di.di
import com.intellij.ide.starter.junit5.TestCleanupListener
import com.intellij.ide.starter.runner.CurrentTestMethod
import com.intellij.lambda.testFramework.utils.IdeWithLambda
import kotlinx.coroutines.job
import org.junit.platform.engine.TestExecutionResult
import org.junit.platform.launcher.TestIdentifier
import org.kodein.di.DI
@@ -16,6 +19,8 @@ import java.nio.file.Path
class LambdaTestExecutionListener : TestCleanupListener() {
companion object {
init {
CommonScope.perSuiteScopeForIdeActivities()
di = DI {
extend(di)
@@ -45,6 +50,5 @@ class LambdaTestExecutionListener : TestCleanupListener() {
if (!testIdentifier.isTest) return
IdeInstance.publishArtifacts()
cancelPerTestSupervisorScope(testIdentifier)
}
}
@@ -1,6 +1,6 @@
package com.intellij.ide.starter.driver.driver.remoteDev
import com.intellij.ide.starter.coroutine.CommonScope.perClassSupervisorScope
import com.intellij.ide.starter.coroutine.CommonScope.scopeForProcesses
import com.intellij.ide.starter.driver.engine.DriverOptions
import com.intellij.ide.starter.driver.engine.remoteDev.XorgWindowManagerHandler
import com.intellij.ide.starter.ide.IDETestContext
@@ -65,7 +65,7 @@ internal class IDEFrontendHandler(
EventsBus.subscribeOnce(process) { event: IdeLaunchEvent ->
process.complete(event.ideProcess)
}
val result = perClassSupervisorScope.async {
val result = scopeForProcesses.async {
try {
val thinClientCommand =
if (frontendContext.ide.vmOptions.data().contains("-Djava.awt.headless=true")) "thinClient-headless" else "thinClient"
@@ -2,7 +2,7 @@ package com.intellij.ide.starter.driver.engine
import com.intellij.driver.client.Driver
import com.intellij.driver.client.impl.JmxHost
import com.intellij.ide.starter.coroutine.CommonScope.perClassSupervisorScope
import com.intellij.ide.starter.coroutine.CommonScope.scopeForProcesses
import com.intellij.ide.starter.ide.IDETestContext
import com.intellij.ide.starter.ide.isRemDevContext
import com.intellij.ide.starter.runner.IDECommandLine
@@ -28,7 +28,7 @@ class LocalDriverRunner : DriverRunner {
EventsBus.subscribeOnce(process) { event: IdeLaunchEvent ->
process.complete(event.ideProcess)
}
val runResult = perClassSupervisorScope.async {
val runResult = scopeForProcesses.async {
Allure.getLifecycle().setCurrentTestCase(currentStep.orElse(UUID.randomUUID().toString()))
try {
context.runIdeSuspending(commandLine,
@@ -1,5 +1,6 @@
package com.intellij.ide.starter.junit5.config
import com.intellij.ide.starter.coroutine.CommonScope
import com.intellij.ide.starter.process.findAndKillLeftoverProcessesFromTestRuns
import org.junit.jupiter.api.extension.AfterAllCallback
import org.junit.jupiter.api.extension.BeforeAllCallback
@@ -7,10 +8,16 @@ import org.junit.jupiter.api.extension.ExtensionContext
open class KillOutdatedProcesses : BeforeAllCallback, AfterAllCallback {
override fun beforeAll(context: ExtensionContext) {
if (!CommonScope.shouldKillOutdatedProcessesBetweenContainers()) {
return
}
findAndKillLeftoverProcessesFromTestRuns()
}
override fun afterAll(context: ExtensionContext) {
if (!CommonScope.shouldKillOutdatedProcessesBetweenContainers()) {
return
}
findAndKillLeftoverProcessesFromTestRuns(reportErrors = true)
}
}
@@ -47,7 +47,8 @@ object CommonScope {
CoroutineScope(SupervisorJob() + Dispatchers.IO + CoroutineName("Test's supervisor scope"))
/**
* Lifespan is limited to the duration of the test class. By the end of the test class whole coroutines tree will be cancelled.
* Lifespan is limited to the duration of the test class. By the end of the test container, a whole coroutines tree will be canceled.
* In a simple case a test container is the same as a class, in case when we have a base class and several classes extend it, the test container is over when all tests extending the base class are over.
*/
val perClassSupervisorScope = CoroutineScope(SupervisorJob() + Dispatchers.IO + CoroutineName("Test class's supervisor scope"))
@@ -56,4 +57,17 @@ object CommonScope {
* In most scenarios you don't need that behavior.
*/
val simpleScope = CoroutineScope(Job() + Dispatchers.IO + CoroutineName("Simple scope"))
var scopeForProcesses: CoroutineScope = perClassSupervisorScope
private set
/*
Processes started in ProcessExecutor and by various runWithDriver will be launched on per suite scope.
It means that they will be stopped when the whole test suite is finished and not after each test container.
*/
fun perSuiteScopeForIdeActivities() {
scopeForProcesses = testSuiteSupervisorScope
}
fun shouldKillOutdatedProcessesBetweenContainers(): Boolean = scopeForProcesses == perClassSupervisorScope
}
@@ -2,7 +2,7 @@ package com.intellij.ide.starter.process.exec
import com.intellij.ide.starter.config.ConfigurationStorage
import com.intellij.ide.starter.config.logEnvVariables
import com.intellij.ide.starter.coroutine.CommonScope.perClassSupervisorScope
import com.intellij.ide.starter.coroutine.CommonScope.scopeForProcesses
import com.intellij.ide.starter.utils.catchAll
import com.intellij.ide.starter.utils.getThrowableText
import com.intellij.tools.ide.util.common.logError
@@ -202,7 +202,7 @@ class ProcessExecutor(
@Suppress("BlockingMethodInNonBlockingContext") val process = processBuilder.start()
val processId = process.pid()
val onProcessCreatedJob: Job = perClassSupervisorScope.launch(Dispatchers.IO + CoroutineName("On process $presentableName created job")) {
val onProcessCreatedJob: Job = scopeForProcesses.launch(Dispatchers.IO + CoroutineName("On process $presentableName created job")) {
if (!silent) logOutput(" ... started external process `$presentableName` with process ID = $processId")
onProcessCreated(process, processId)
}