[LL] CleanableSoftValueCache: Add non-concurrent tests for value cleanup

- The non-concurrent `CleanableSoftValueCacheTest` ensures that
  deterministic cleanup behaves correctly. A Lincheck test can only
  discover differences between the single-threaded and concurrent
  executions of a test scenario, so it cannot find correctness issues
  with deterministic cleanup on its own.

^KT-61222
This commit is contained in:
Marco Pennekamp
2024-01-17 17:26:23 +01:00
committed by Space Team
parent 53e545aa3f
commit 5207940f0b
4 changed files with 282 additions and 12 deletions
@@ -5,8 +5,10 @@
package org.jetbrains.kotlin.analysis.test.framework.utils
import com.intellij.mock.MockApplication
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.util.Computable
import com.intellij.openapi.util.Disposer
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiReference
import com.intellij.psi.impl.source.resolve.reference.impl.PsiMultiReference
@@ -16,6 +18,7 @@ import org.jetbrains.kotlin.idea.references.KtReference
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.test.directives.model.Directive
import org.jetbrains.kotlin.test.directives.model.RegisteredDirectives
import org.jetbrains.kotlin.test.testFramework.resetApplicationToNull
inline fun <T> runReadAction(crossinline runnable: () -> T): T {
return ApplicationManager.getApplication().runReadAction(Computable { runnable() })
@@ -24,6 +27,30 @@ inline fun <T> runReadAction(crossinline runnable: () -> T): T {
fun <R> executeOnPooledThreadInReadAction(action: () -> R): R =
ApplicationManager.getApplication().executeOnPooledThread<R> { runReadAction(action) }.get()
/**
* Executes [action] with a dummy application available via [ApplicationManager.getApplication]. This function should **only** be used if
* an application is needed to avoid null pointer exceptions from [ApplicationManager.getApplication] when used in simple situations. Do not
* use this function if you need a properly set up application and project.
*/
fun <R> withDummyApplication(action: () -> R): R {
val previousApplication = ApplicationManager.getApplication()
val disposable = Disposer.newDisposable("Application disposable for dummy application from Analysis API test framework")
try {
MockApplication.setUp(disposable)
return action()
} finally {
Disposer.dispose(disposable)
// If there is a previous application, disposal of `disposable` will reset the application manager to that application, so we won't
// need to nor should we reset its application to `null`. This is handled by the application argument in `resetApplicationToNull`.
resetApplicationToNull(previousApplication)
require(ApplicationManager.getApplication() === previousApplication) {
"The managed application should have been reset to the previous application or `null`."
}
}
}
fun PsiElement?.position(): String {
if (this == null) return "(unknown)"
return offsetToLineAndColumn(containingFile.viewProvider.document, textRange.startOffset).toString()