[Test] Reset ApplicationManager.application when shared application environments are disposed
- Shared application environments can be disposed in the middle of test plan execution when the project count reaches zero. So resetting `ApplicationManager` only on "test plan execution finished" is incorrect, because it does not account for that scenario. Instead, we should reset `ApplicationManager` every time the application environment is disposed. - We should also keep the manual reset in `testPlanExecutionFinished` because `disposeApplicationEnvironment` may not need to dispose anything if there isn't a shared application environment, but we should still reset `ApplicationManager` if an unshared application environment was created. - This fixes "Some test disposed, but forgot to clear MockApplication" exceptions which appeared after KT-64099 because it opened up proper disposal of the shared application environment (instead of it being in a state of leakage because not all project disposables were properly disposed). ^KT-63650 ^KT-64099
This commit is contained in:
committed by
Space Team
parent
85c9c57da4
commit
dcf7b84082
+28
@@ -16,6 +16,7 @@ import com.intellij.ide.highlighter.JavaFileType
|
|||||||
import com.intellij.lang.java.JavaParserDefinition
|
import com.intellij.lang.java.JavaParserDefinition
|
||||||
import com.intellij.mock.MockProject
|
import com.intellij.mock.MockProject
|
||||||
import com.intellij.openapi.Disposable
|
import com.intellij.openapi.Disposable
|
||||||
|
import com.intellij.openapi.application.Application
|
||||||
import com.intellij.openapi.application.ApplicationManager
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
import com.intellij.openapi.application.TransactionGuard
|
import com.intellij.openapi.application.TransactionGuard
|
||||||
import com.intellij.openapi.application.TransactionGuardImpl
|
import com.intellij.openapi.application.TransactionGuardImpl
|
||||||
@@ -594,10 +595,37 @@ class KotlinCoreEnvironment private constructor(
|
|||||||
val environment = ourApplicationEnvironment ?: return
|
val environment = ourApplicationEnvironment ?: return
|
||||||
ourApplicationEnvironment = null
|
ourApplicationEnvironment = null
|
||||||
Disposer.dispose(environment.parentDisposable)
|
Disposer.dispose(environment.parentDisposable)
|
||||||
|
resetApplicationManager(environment.application)
|
||||||
ZipHandler.clearFileAccessorCache()
|
ZipHandler.clearFileAccessorCache()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the application managed by [ApplicationManager] to `null`. If [applicationToReset] is specified, [resetApplicationManager]
|
||||||
|
* will only reset the application if it's the expected one. Otherwise, the application will already have been changed to another
|
||||||
|
* application. For example, application disposal can trigger one of the disposables registered via
|
||||||
|
* [ApplicationManager.setApplication], which reset the managed application to the previous application.
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun resetApplicationManager(applicationToReset: Application? = null) {
|
||||||
|
val currentApplication = ApplicationManager.getApplication() ?: return
|
||||||
|
if (applicationToReset != null && applicationToReset != currentApplication) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
val ourApplicationField = ApplicationManager::class.java.getDeclaredField("ourApplication")
|
||||||
|
ourApplicationField.isAccessible = true
|
||||||
|
ourApplicationField.set(null, null)
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
// Resetting the application manager is not critical in a production context. If the reflective access fails, we shouldn't
|
||||||
|
// expose the user to the failure.
|
||||||
|
if (currentApplication.isUnitTestMode) {
|
||||||
|
throw exception
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun ProjectEnvironment.configureProjectEnvironment(
|
fun ProjectEnvironment.configureProjectEnvironment(
|
||||||
configuration: CompilerConfiguration,
|
configuration: CompilerConfiguration,
|
||||||
|
|||||||
+1
-5
@@ -6,13 +6,11 @@
|
|||||||
package org.jetbrains.kotlin.test
|
package org.jetbrains.kotlin.test
|
||||||
|
|
||||||
import com.intellij.openapi.Disposable
|
import com.intellij.openapi.Disposable
|
||||||
import com.intellij.openapi.application.ApplicationManager
|
|
||||||
import com.intellij.openapi.util.Disposer
|
import com.intellij.openapi.util.Disposer
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||||
import org.jetbrains.kotlin.test.services.ApplicationDisposableProvider
|
import org.jetbrains.kotlin.test.services.ApplicationDisposableProvider
|
||||||
import org.junit.platform.launcher.TestExecutionListener
|
import org.junit.platform.launcher.TestExecutionListener
|
||||||
import org.junit.platform.launcher.TestPlan
|
import org.junit.platform.launcher.TestPlan
|
||||||
import java.lang.reflect.Field
|
|
||||||
|
|
||||||
class ApplicationEnvironmentDisposer : TestExecutionListener {
|
class ApplicationEnvironmentDisposer : TestExecutionListener {
|
||||||
companion object {
|
companion object {
|
||||||
@@ -22,9 +20,7 @@ class ApplicationEnvironmentDisposer : TestExecutionListener {
|
|||||||
override fun testPlanExecutionFinished(testPlan: TestPlan) {
|
override fun testPlanExecutionFinished(testPlan: TestPlan) {
|
||||||
KotlinCoreEnvironment.disposeApplicationEnvironment()
|
KotlinCoreEnvironment.disposeApplicationEnvironment()
|
||||||
Disposer.dispose(ROOT_DISPOSABLE)
|
Disposer.dispose(ROOT_DISPOSABLE)
|
||||||
val ourApplicationField: Field = ApplicationManager::class.java.getDeclaredField("ourApplication")
|
KotlinCoreEnvironment.resetApplicationManager()
|
||||||
ourApplicationField.isAccessible = true
|
|
||||||
ourApplicationField.set(null, null)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-5
@@ -6,9 +6,8 @@
|
|||||||
package org.jetbrains.kotlin.test.testFramework
|
package org.jetbrains.kotlin.test.testFramework
|
||||||
|
|
||||||
import com.intellij.openapi.application.Application
|
import com.intellij.openapi.application.Application
|
||||||
import com.intellij.openapi.application.ApplicationManager
|
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||||
import org.jetbrains.kotlin.utils.rethrow
|
import org.jetbrains.kotlin.utils.rethrow
|
||||||
import java.lang.reflect.Field
|
|
||||||
|
|
||||||
fun resetApplicationToNull(old: Application?) {
|
fun resetApplicationToNull(old: Application?) {
|
||||||
if (old != null) return
|
if (old != null) return
|
||||||
@@ -17,9 +16,7 @@ fun resetApplicationToNull(old: Application?) {
|
|||||||
|
|
||||||
fun resetApplicationToNull() {
|
fun resetApplicationToNull() {
|
||||||
try {
|
try {
|
||||||
val ourApplicationField: Field = ApplicationManager::class.java.getDeclaredField("ourApplication")
|
KotlinCoreEnvironment.resetApplicationManager()
|
||||||
ourApplicationField.setAccessible(true)
|
|
||||||
ourApplicationField.set(null, null)
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
throw rethrow(e)
|
throw rethrow(e)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user