[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.mock.MockProject
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.application.Application
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.application.TransactionGuard
|
||||
import com.intellij.openapi.application.TransactionGuardImpl
|
||||
@@ -594,10 +595,37 @@ class KotlinCoreEnvironment private constructor(
|
||||
val environment = ourApplicationEnvironment ?: return
|
||||
ourApplicationEnvironment = null
|
||||
Disposer.dispose(environment.parentDisposable)
|
||||
resetApplicationManager(environment.application)
|
||||
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
|
||||
fun ProjectEnvironment.configureProjectEnvironment(
|
||||
configuration: CompilerConfiguration,
|
||||
|
||||
Reference in New Issue
Block a user