Skip to content

Use proper disposable for connection to message bus #390

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Aug 30, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
package org.utbot.cpp.clion.plugin.client

import com.intellij.openapi.Disposable
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.components.Service
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Disposer
import org.utbot.cpp.clion.plugin.client.channels.GTestLogChannelImpl
import org.utbot.cpp.clion.plugin.client.channels.LogChannel
import org.utbot.cpp.clion.plugin.client.channels.ServerLogChannelImpl
import kotlin.random.Random
import org.utbot.cpp.clion.plugin.listeners.ConnectionSettingsListener
import org.utbot.cpp.clion.plugin.utils.logger
import org.utbot.cpp.clion.plugin.utils.projectLifetimeDisposable

@Service
class ClientManager(val project: Project) : Disposable {
Expand All @@ -19,11 +20,12 @@ class ClientManager(val project: Project) : Disposable {
private set

init {
Disposer.register(this, project.projectLifetimeDisposable)
subscribeToEvents()
}

private fun subscribeToEvents() {
with(ApplicationManager.getApplication().messageBus.connect()) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here was the cause for exception: after closing project this listeners was not disposed, because it had application lifetime

with(project.messageBus.connect(project.projectLifetimeDisposable)) {
subscribe(ConnectionSettingsListener.TOPIC, object : ConnectionSettingsListener {
override fun connectionSettingsChanged(newPort: Int, newServerName: String) {
if (newPort != client.port || newServerName != client.serverName) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,11 @@ class CoverageAndResultsHandler(
}

// when we received results, test statuses should be updated in the gutter
project.messageBus.syncPublisher(UTBotTestResultsReceivedListener.TOPIC)
.testResultsReceived(response.testRunResultsList)
project.messageBus.let { bus ->
if (!bus.isDisposed)
bus.syncPublisher(UTBotTestResultsReceivedListener.TOPIC)
.testResultsReceived(response.testRunResultsList)
}

val engine = CoverageEngine.EP_NAME.findExtension(UTBotCoverageEngine::class.java)
?: error("UTBotEngine instance is not found!")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,4 @@ class ConsoleWriter(project: Project) : LogWriter {
console.print(formattedMessage, logLevel)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,10 @@ class UTBotAllProjectSettings(val project: Project) {
}

fun fireUTBotSettingsChanged() {
project.messageBus.syncPublisher(UTBotSettingsChangedListener.TOPIC).settingsChanged(this)
project.messageBus.let { bus ->
if(!bus.isDisposed)
bus.syncPublisher(UTBotSettingsChangedListener.TOPIC).settingsChanged(this)
}
}

fun predictPaths() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ package org.utbot.cpp.clion.plugin.settings

import com.intellij.openapi.components.service
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory
import com.intellij.openapi.options.BoundConfigurable
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.DialogPanel
Expand All @@ -14,7 +13,6 @@ import com.intellij.ui.dsl.builder.BottomGap
import com.intellij.ui.dsl.builder.COLUMNS_LARGE
import com.intellij.ui.dsl.builder.LabelPosition
import com.intellij.ui.dsl.builder.Panel
import com.intellij.ui.dsl.builder.Row
import com.intellij.ui.dsl.builder.bindIntValue
import com.intellij.ui.dsl.builder.bindSelected
import com.intellij.ui.dsl.builder.bindText
Expand All @@ -25,6 +23,7 @@ import org.utbot.cpp.clion.plugin.UTBot
import org.utbot.cpp.clion.plugin.listeners.UTBotSettingsChangedListener
import org.utbot.cpp.clion.plugin.ui.sourceFoldersView.UTBotProjectViewPaneForSettings
import org.utbot.cpp.clion.plugin.utils.commandLineEditor
import org.utbot.cpp.clion.plugin.utils.projectLifetimeDisposable
import java.awt.Dimension

class UTBotConfigurable(private val myProject: Project) : BoundConfigurable(
Expand All @@ -39,24 +38,14 @@ class UTBotConfigurable(private val myProject: Project) : BoundConfigurable(


init {
myProject.messageBus.connect()
myProject.messageBus.connect(myProject.projectLifetimeDisposable)
.subscribe(UTBotSettingsChangedListener.TOPIC, UTBotSettingsChangedListener {
reset()
})
}

override fun createPanel() = panel

private fun Panel.createPathChooser(property: KMutableProperty0<String>, name: String, chooserTitle: String): Row {
return row(name) {
textFieldWithBrowseButton(
chooserTitle,
myProject,
FileChooserDescriptorFactory.createSingleFileDescriptor()
).bindText(property).columns(COLUMNS_LARGE)
}
}

private fun createMainPanel(): DialogPanel {
logger.trace("createPanel was called")
return panel {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
package org.utbot.cpp.clion.plugin.ui.services

import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer
import com.intellij.openapi.Disposable
import com.intellij.openapi.components.Service
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Disposer
import org.utbot.cpp.clion.plugin.client.handlers.SourceCode
import org.utbot.cpp.clion.plugin.listeners.UTBotTestResultsReceivedListener
import org.utbot.cpp.clion.plugin.utils.convertFromRemotePathIfNeeded
import org.utbot.cpp.clion.plugin.utils.projectLifetimeDisposable
import testsgen.Testgen
import java.util.concurrent.ConcurrentHashMap

@Service
class TestsResultsStorage(val project: Project) {
class TestsResultsStorage(val project: Project) : Disposable {
private val storage: MutableMap<String, Testgen.TestResultObject> = ConcurrentHashMap(mutableMapOf())
private val log = Logger.getInstance(this::class.java)

init {
val connection = project.messageBus.connect()
connection.subscribe(
Disposer.register(project.projectLifetimeDisposable, this)
project.messageBus.connect(this).subscribe(
UTBotTestResultsReceivedListener.TOPIC,
UTBotTestResultsReceivedListener { results ->
log.info("Received results")
Expand Down Expand Up @@ -58,4 +61,8 @@ class TestsResultsStorage(val project: Project) {
if (shouldForceUpdate())
DaemonCodeAnalyzer.getInstance(project).restart()
}

override fun dispose() {
storage.clear()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import org.utbot.cpp.clion.plugin.listeners.SourceFoldersListener
import org.utbot.cpp.clion.plugin.settings.settings
import org.utbot.cpp.clion.plugin.utils.localPath
import javax.swing.tree.DefaultTreeModel
import org.utbot.cpp.clion.plugin.utils.projectLifetimeDisposable

open class UTBotProjectViewPane(project: Project) : ProjectViewPane(project) {
override fun enableDnD() = Unit
Expand All @@ -19,8 +20,9 @@ open class UTBotProjectViewPane(project: Project) : ProjectViewPane(project) {
override fun getPresentableSubIdName(subId: String): String = "UTBotSourceDirectoriesPane"

init {
// when sourceDirs are updated in model, update view
project.messageBus.connect().subscribe(SourceFoldersListener.TOPIC, SourceFoldersListener {
// this connection is needed during project lifetime, so using projectLifetimeDisposable as parent disposable
project.messageBus.connect(project.projectLifetimeDisposable).subscribe(SourceFoldersListener.TOPIC, SourceFoldersListener {
// when sourceDirs are updated in model, update view
// it will eventually call node.update, see UTBotNode
updateFromRoot(true)
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ import com.intellij.openapi.wm.StatusBarWidget
import com.intellij.openapi.wm.StatusBarWidgetFactory
import com.intellij.ui.awt.RelativePoint
import com.intellij.util.Consumer
import org.utbot.cpp.clion.plugin.actions.AskServerToGenerateJsonForProjectConfiguration
import org.utbot.cpp.clion.plugin.actions.ReconnectAction
import org.utbot.cpp.clion.plugin.actions.configure.ConfigureProjectAction
import org.utbot.cpp.clion.plugin.actions.configure.ReconfigureProjectAction
import org.utbot.cpp.clion.plugin.actions.ShowWizardAction
import org.utbot.cpp.clion.plugin.listeners.ConnectionStatus
import org.utbot.cpp.clion.plugin.listeners.UTBotEventsListener
import org.utbot.cpp.clion.plugin.utils.projectLifetimeDisposable
import java.awt.Component
import java.awt.Point
import java.awt.event.MouseEvent
Expand Down Expand Up @@ -50,14 +50,17 @@ class UTBotStatusBarWidget : StatusBarWidget, StatusBarWidget.TextPresentation {

override fun install(statusbar: StatusBar) {
this.statusBar = statusbar
statusbar.project?.messageBus?.connect()?.subscribe(
UTBotEventsListener.CONNECTION_CHANGED_TOPIC,
object : UTBotEventsListener {
override fun onConnectionChange(oldStatus: ConnectionStatus, newStatus: ConnectionStatus) {
myConnectionStatusText = newStatus.description
statusBar?.updateWidget(ID())
}
})
statusbar.project?.let { project ->
// use project level service as disposable
project.messageBus.connect(project.projectLifetimeDisposable).subscribe(
UTBotEventsListener.CONNECTION_CHANGED_TOPIC,
object : UTBotEventsListener {
override fun onConnectionChange(oldStatus: ConnectionStatus, newStatus: ConnectionStatus) {
myConnectionStatusText = newStatus.description
statusBar?.updateWidget(ID())
}
})
}
}

override fun dispose() {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import com.intellij.openapi.wm.StatusBarWidget
import com.intellij.util.Consumer
import org.utbot.cpp.clion.plugin.listeners.UTBotSettingsChangedListener
import org.utbot.cpp.clion.plugin.settings.settings
import org.utbot.cpp.clion.plugin.utils.projectLifetimeDisposable
import java.awt.Component
import java.awt.event.MouseEvent

Expand All @@ -16,9 +17,12 @@ class UTBotStatusBarVerboseWidget : StatusBarWidget, StatusBarWidget.TextPresent
override fun install(statusbar: StatusBar) {
this.statusBar = statusbar
statusBar?.updateWidget(ID())
statusbar.project?.messageBus?.connect()?.subscribe(UTBotSettingsChangedListener.TOPIC, UTBotSettingsChangedListener {
statusbar.updateWidget(ID())
})
statusbar.project?.let { project ->
project.messageBus.connect(project.projectLifetimeDisposable)
.subscribe(UTBotSettingsChangedListener.TOPIC, UTBotSettingsChangedListener {
statusbar.updateWidget(ID())
})
}
}

override fun dispose() {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import org.utbot.cpp.clion.plugin.settings.settings
import org.utbot.cpp.clion.plugin.utils.getCurrentClient
import org.utbot.cpp.clion.plugin.utils.invokeOnEdt
import org.utbot.cpp.clion.plugin.utils.logger
import org.utbot.cpp.clion.plugin.utils.projectLifetimeDisposable
import testsgen.Testgen

@Service
Expand Down Expand Up @@ -98,18 +99,17 @@ class UTBotTargetsController(val project: Project) {
}

private fun connectToEvents() {
project.messageBus.connect().also { connection ->
// this connection is needed during project lifetime so we pass this service as parent disposable
project.messageBus.connect(project.projectLifetimeDisposable).subscribe(
// when we reconnected to server, the targets should be updated, so we request them from server
connection.subscribe(
UTBotEventsListener.CONNECTION_CHANGED_TOPIC,
object : UTBotEventsListener {
override fun onConnectionChange(oldStatus: ConnectionStatus, newStatus: ConnectionStatus) {
if (newStatus != oldStatus) {
requestTargetsFromServer()
}
UTBotEventsListener.CONNECTION_CHANGED_TOPIC,
object : UTBotEventsListener {
override fun onConnectionChange(oldStatus: ConnectionStatus, newStatus: ConnectionStatus) {
if (newStatus != oldStatus) {
requestTargetsFromServer()
}
}
)
}
}
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.utbot.cpp.clion.plugin.utils

import com.intellij.openapi.Disposable
import com.intellij.openapi.components.Service
import com.intellij.openapi.components.service
import com.intellij.openapi.project.Project

/**
* A project level service to be used as parent disposable
* for disposables (objects that need to do some cleanup) with project lifetime
*
* Must not be used as a child disposable, because it is a child for [ClientManager] disposable
*/
@Service
class ProjectLifetimeDisposable(project: Project) : Disposable {
override fun dispose() {}
}

val Project.projectLifetimeDisposable: ProjectLifetimeDisposable get() = this.service()
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import com.intellij.testFramework.fixtures.CodeInsightTestFixture
import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory
import com.intellij.testFramework.fixtures.impl.TempDirTestFixtureImpl
import com.intellij.util.io.delete
import kotlinx.coroutines.runBlocking
import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.TestInstance
Expand Down Expand Up @@ -98,10 +97,11 @@ abstract class BaseGenerationTestCase {
/**
* Waits until all requests initiated during tests are finished
*/
fun waitForRequestsToFinish() = runBlocking {
fun waitForRequestsToFinish() {
// requests to server are asynchronous, need to wait for server to respond
client.waitForServerRequestsToFinish(ifNotFinished = { unfinishedCoroutines: List<Job> ->
// some requests may be executed only on EDT, so we wk
// some requests may be executed only on EDT, so we flush the queue. Otherwise,
// these requests won't finish
PlatformTestUtil.dispatchAllInvocationEventsInIdeEventQueue()
logger.info("Waiting for requests to finish: $unfinishedCoroutines")
})
Expand Down