diff --git a/java/idea-ui/intellij.java.ui.tests.iml b/java/idea-ui/intellij.java.ui.tests.iml index ad172adfd097..3faf05d01266 100644 --- a/java/idea-ui/intellij.java.ui.tests.iml +++ b/java/idea-ui/intellij.java.ui.tests.iml @@ -21,5 +21,6 @@ + \ No newline at end of file diff --git a/java/idea-ui/src/com/intellij/jarRepository/LibrarySynchronizationQueue.kt b/java/idea-ui/src/com/intellij/jarRepository/LibrarySynchronizationQueue.kt index fc1cf9e92244..c6f9b2201206 100644 --- a/java/idea-ui/src/com/intellij/jarRepository/LibrarySynchronizationQueue.kt +++ b/java/idea-ui/src/com/intellij/jarRepository/LibrarySynchronizationQueue.kt @@ -17,7 +17,7 @@ import org.jetbrains.idea.maven.utils.library.RepositoryUtils import kotlin.coroutines.coroutineContext @Service(Service.Level.PROJECT) -internal class LibrarySynchronizationQueue(private val project: Project, private val scope: CoroutineScope) { +class LibrarySynchronizationQueue(private val project: Project, private val scope: CoroutineScope) { private val synchronizationRequests = Channel(capacity = Channel.UNLIMITED).apply { scope.coroutineContext.job.invokeOnCompletion { close() @@ -84,7 +84,11 @@ internal class LibrarySynchronizationQueue(private val project: Project, private if (!coroutineContext.isActive) { return } - if (readAction { library.needToReload() }) { + val needToReload = readAction { + if (library.isDisposed) return@readAction false + library.needToReload() + } + if (needToReload) { RepositoryUtils.reloadDependencies(project, library) } } diff --git a/java/idea-ui/testSrc/com/intellij/jarRepository/RepositoryLibraryTest.kt b/java/idea-ui/testSrc/com/intellij/jarRepository/RepositoryLibraryTest.kt index 6019b424bf74..b6305741b531 100644 --- a/java/idea-ui/testSrc/com/intellij/jarRepository/RepositoryLibraryTest.kt +++ b/java/idea-ui/testSrc/com/intellij/jarRepository/RepositoryLibraryTest.kt @@ -2,53 +2,64 @@ package com.intellij.jarRepository import com.intellij.java.library.getMavenCoordinates +import com.intellij.openapi.Disposable import com.intellij.openapi.application.runWriteActionAndWait import com.intellij.openapi.roots.OrderRootType import com.intellij.openapi.roots.impl.libraries.LibraryEx import com.intellij.openapi.roots.libraries.Library import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar +import com.intellij.openapi.util.Disposer import com.intellij.openapi.vfs.VfsUtil import com.intellij.platform.backend.workspace.WorkspaceModel import com.intellij.platform.backend.workspace.impl.internal -import com.intellij.testFramework.ApplicationRule -import com.intellij.testFramework.DisposableRule -import com.intellij.testFramework.RuleChain -import com.intellij.testFramework.rules.ProjectModelRule -import com.intellij.testFramework.rules.TempDirectory +import com.intellij.testFramework.junit5.TestApplication +import com.intellij.testFramework.junit5.TestDisposable +import com.intellij.testFramework.rules.ProjectModelExtension +import com.intellij.testFramework.rules.TempDirectoryExtension +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking import org.jetbrains.idea.maven.utils.library.RepositoryLibraryProperties import org.jetbrains.idea.maven.utils.library.RepositoryUtils -import org.junit.Assert.* -import org.junit.Before -import org.junit.ClassRule -import org.junit.Rule -import org.junit.Test +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertDoesNotThrow +import org.junit.jupiter.api.extension.RegisterExtension import java.util.concurrent.TimeUnit import kotlin.io.path.exists +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertNull +import kotlin.test.assertTrue +@TestApplication class RepositoryLibraryTest { - companion object { - @JvmStatic - @get:ClassRule - val applicationRule = ApplicationRule() - } + @TestDisposable + lateinit var disposable: Disposable - private val disposableRule = DisposableRule() - private val projectRule = ProjectModelRule() - private val mavenRepo = TempDirectory() - private val localMavenCache = TempDirectory() - @get:Rule - val rulesChain = RuleChain(localMavenCache, mavenRepo, projectRule, disposableRule) + @JvmField + @RegisterExtension + val projectRule: ProjectModelExtension = ProjectModelExtension() + + @JvmField + @RegisterExtension + val mavenRepo = TempDirectoryExtension() + + @JvmField + @RegisterExtension + val localMavenCache = TempDirectoryExtension() private val ARTIFACT_NAME = "myArtifact" private val GROUP_NAME = "myGroup" private val LIBRARY_NAME = "NewLibrary" - @Before + @BeforeEach fun setUp() { JarRepositoryManager.setLocalRepositoryPath(localMavenCache.root) MavenRepoFixture(mavenRepo.root).apply { addLibraryArtifact(group = GROUP_NAME, artifact = ARTIFACT_NAME, version = "1.0") + addLibraryArtifact(group = GROUP_NAME, artifact = ARTIFACT_NAME, version = "1.0-SNAPSHOT") generateMavenMetadata(GROUP_NAME, ARTIFACT_NAME) } @@ -57,17 +68,21 @@ class RepositoryLibraryTest { ) } - private fun createLibrary(block: (LibraryEx.ModifiableModelEx) -> Unit = {}): Library { + private fun createLibrary(version: String = "1.0", libraryName: String = LIBRARY_NAME, block: (LibraryEx.ModifiableModelEx) -> Unit = {}): Library { val libraryTable = LibraryTablesRegistrar.getInstance().getLibraryTable(projectRule.project) val library = runWriteActionAndWait { - projectRule.addProjectLevelLibrary(LIBRARY_NAME) { + projectRule.addProjectLevelLibrary(libraryName) { it.kind = RepositoryLibraryType.REPOSITORY_LIBRARY_KIND - it.properties = RepositoryLibraryProperties(GROUP_NAME, ARTIFACT_NAME, "1.0", false, emptyList()) + it.properties = RepositoryLibraryProperties(GROUP_NAME, ARTIFACT_NAME, version, false, emptyList()) block(it) } } - disposableRule.register { - runWriteActionAndWait { libraryTable.removeLibrary(library) } + Disposer.register(disposable) { + runWriteActionAndWait { + if (!library.isDisposed) { + libraryTable.removeLibrary(library) + } + } } return library } @@ -105,6 +120,24 @@ class RepositoryLibraryTest { assertTrue(workspaceVersion() > modelVersionBefore) } + @Test + fun testSynchronizationQueueDoesWorkWithDisposedLibs() = runBlocking { + localMavenCache.rootPath.resolve(GROUP_NAME).resolve(ARTIFACT_NAME).resolve("1.0").resolve("$ARTIFACT_NAME-1.0.jar") + + repeat(1) { + val library = createLibrary(version = "1.0-SNAPSHOT", libraryName = "Lib$it") + assertEquals(0, getLibraryRoots(library).size) + assertDoesNotThrow { + val deferred = launch { + LibrarySynchronizationQueue.getInstance(projectRule.project).requestSynchronization(library as LibraryEx) + LibrarySynchronizationQueue.getInstance(projectRule.project).flush() + } + Disposer.dispose(library) + deferred.join() + } + } + } + @Test fun libraryNoUpdateProjectModel() { val jar = localMavenCache.rootPath.resolve(GROUP_NAME).resolve(ARTIFACT_NAME).resolve("1.0").resolve("$ARTIFACT_NAME-1.0.jar")