Skip to content

Commit 6cc816c

Browse files
authored
CLion plugin: Use proper disposable for connection to message bus (#390)
* fix * required changes * Add projectLifeTimeDisposable to be used as project level disposable * fix comment
1 parent 9216114 commit 6cc816c

File tree

12 files changed

+81
-49
lines changed

12 files changed

+81
-49
lines changed

clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/client/ClientManager.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
package org.utbot.cpp.clion.plugin.client
22

33
import com.intellij.openapi.Disposable
4-
import com.intellij.openapi.application.ApplicationManager
54
import com.intellij.openapi.components.Service
65
import com.intellij.openapi.project.Project
6+
import com.intellij.openapi.util.Disposer
77
import org.utbot.cpp.clion.plugin.client.channels.GTestLogChannelImpl
88
import org.utbot.cpp.clion.plugin.client.channels.LogChannel
99
import org.utbot.cpp.clion.plugin.client.channels.ServerLogChannelImpl
1010
import kotlin.random.Random
1111
import org.utbot.cpp.clion.plugin.listeners.ConnectionSettingsListener
1212
import org.utbot.cpp.clion.plugin.utils.logger
13+
import org.utbot.cpp.clion.plugin.utils.projectLifetimeDisposable
1314

1415
@Service
1516
class ClientManager(val project: Project) : Disposable {
@@ -19,11 +20,12 @@ class ClientManager(val project: Project) : Disposable {
1920
private set
2021

2122
init {
23+
Disposer.register(this, project.projectLifetimeDisposable)
2224
subscribeToEvents()
2325
}
2426

2527
private fun subscribeToEvents() {
26-
with(ApplicationManager.getApplication().messageBus.connect()) {
28+
with(project.messageBus.connect(project.projectLifetimeDisposable)) {
2729
subscribe(ConnectionSettingsListener.TOPIC, object : ConnectionSettingsListener {
2830
override fun connectionSettingsChanged(newPort: Int, newServerName: String) {
2931
if (newPort != client.port || newServerName != client.serverName) {

clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/client/handlers/CoverageAndResultsHandler.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,11 @@ class CoverageAndResultsHandler(
7272
}
7373

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

7881
val engine = CoverageEngine.EP_NAME.findExtension(UTBotCoverageEngine::class.java)
7982
?: error("UTBotEngine instance is not found!")

clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/client/logger/LogWriter.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,4 @@ class ConsoleWriter(project: Project) : LogWriter {
3131
console.print(formattedMessage, logLevel)
3232
}
3333
}
34-
}
34+
}

clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/settings/UTBotAllProjectSettings.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,10 @@ class UTBotAllProjectSettings(val project: Project) {
5151
}
5252

5353
fun fireUTBotSettingsChanged() {
54-
project.messageBus.syncPublisher(UTBotSettingsChangedListener.TOPIC).settingsChanged(this)
54+
project.messageBus.let { bus ->
55+
if(!bus.isDisposed)
56+
bus.syncPublisher(UTBotSettingsChangedListener.TOPIC).settingsChanged(this)
57+
}
5558
}
5659

5760
fun predictPaths() {

clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/settings/UTBotConfigurable.kt

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ package org.utbot.cpp.clion.plugin.settings
44

55
import com.intellij.openapi.components.service
66
import com.intellij.openapi.diagnostic.Logger
7-
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory
87
import com.intellij.openapi.options.BoundConfigurable
98
import com.intellij.openapi.project.Project
109
import com.intellij.openapi.ui.DialogPanel
@@ -14,7 +13,6 @@ import com.intellij.ui.dsl.builder.BottomGap
1413
import com.intellij.ui.dsl.builder.COLUMNS_LARGE
1514
import com.intellij.ui.dsl.builder.LabelPosition
1615
import com.intellij.ui.dsl.builder.Panel
17-
import com.intellij.ui.dsl.builder.Row
1816
import com.intellij.ui.dsl.builder.bindIntValue
1917
import com.intellij.ui.dsl.builder.bindSelected
2018
import com.intellij.ui.dsl.builder.bindText
@@ -25,6 +23,7 @@ import org.utbot.cpp.clion.plugin.UTBot
2523
import org.utbot.cpp.clion.plugin.listeners.UTBotSettingsChangedListener
2624
import org.utbot.cpp.clion.plugin.ui.sourceFoldersView.UTBotProjectViewPaneForSettings
2725
import org.utbot.cpp.clion.plugin.utils.commandLineEditor
26+
import org.utbot.cpp.clion.plugin.utils.projectLifetimeDisposable
2827
import java.awt.Dimension
2928

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

4039

4140
init {
42-
myProject.messageBus.connect()
41+
myProject.messageBus.connect(myProject.projectLifetimeDisposable)
4342
.subscribe(UTBotSettingsChangedListener.TOPIC, UTBotSettingsChangedListener {
4443
reset()
4544
})
4645
}
4746

4847
override fun createPanel() = panel
4948

50-
private fun Panel.createPathChooser(property: KMutableProperty0<String>, name: String, chooserTitle: String): Row {
51-
return row(name) {
52-
textFieldWithBrowseButton(
53-
chooserTitle,
54-
myProject,
55-
FileChooserDescriptorFactory.createSingleFileDescriptor()
56-
).bindText(property).columns(COLUMNS_LARGE)
57-
}
58-
}
59-
6049
private fun createMainPanel(): DialogPanel {
6150
logger.trace("createPanel was called")
6251
return panel {

clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/ui/services/TestsResultsStorage.kt

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,27 @@
11
package org.utbot.cpp.clion.plugin.ui.services
22

33
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer
4+
import com.intellij.openapi.Disposable
45
import com.intellij.openapi.components.Service
56
import com.intellij.openapi.diagnostic.Logger
67
import com.intellij.openapi.fileEditor.FileEditorManager
78
import com.intellij.openapi.project.Project
9+
import com.intellij.openapi.util.Disposer
810
import org.utbot.cpp.clion.plugin.client.handlers.SourceCode
911
import org.utbot.cpp.clion.plugin.listeners.UTBotTestResultsReceivedListener
1012
import org.utbot.cpp.clion.plugin.utils.convertFromRemotePathIfNeeded
13+
import org.utbot.cpp.clion.plugin.utils.projectLifetimeDisposable
1114
import testsgen.Testgen
1215
import java.util.concurrent.ConcurrentHashMap
1316

1417
@Service
15-
class TestsResultsStorage(val project: Project) {
18+
class TestsResultsStorage(val project: Project) : Disposable {
1619
private val storage: MutableMap<String, Testgen.TestResultObject> = ConcurrentHashMap(mutableMapOf())
1720
private val log = Logger.getInstance(this::class.java)
1821

1922
init {
20-
val connection = project.messageBus.connect()
21-
connection.subscribe(
23+
Disposer.register(project.projectLifetimeDisposable, this)
24+
project.messageBus.connect(this).subscribe(
2225
UTBotTestResultsReceivedListener.TOPIC,
2326
UTBotTestResultsReceivedListener { results ->
2427
log.info("Received results")
@@ -58,4 +61,8 @@ class TestsResultsStorage(val project: Project) {
5861
if (shouldForceUpdate())
5962
DaemonCodeAnalyzer.getInstance(project).restart()
6063
}
64+
65+
override fun dispose() {
66+
storage.clear()
67+
}
6168
}

clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/ui/sourceFoldersView/UTBotProjectViewPane.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import org.utbot.cpp.clion.plugin.listeners.SourceFoldersListener
1111
import org.utbot.cpp.clion.plugin.settings.settings
1212
import org.utbot.cpp.clion.plugin.utils.localPath
1313
import javax.swing.tree.DefaultTreeModel
14+
import org.utbot.cpp.clion.plugin.utils.projectLifetimeDisposable
1415

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

2122
init {
22-
// when sourceDirs are updated in model, update view
23-
project.messageBus.connect().subscribe(SourceFoldersListener.TOPIC, SourceFoldersListener {
23+
// this connection is needed during project lifetime, so using projectLifetimeDisposable as parent disposable
24+
project.messageBus.connect(project.projectLifetimeDisposable).subscribe(SourceFoldersListener.TOPIC, SourceFoldersListener {
25+
// when sourceDirs are updated in model, update view
2426
// it will eventually call node.update, see UTBotNode
2527
updateFromRoot(true)
2628
})

clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/ui/statusBar/StatusBarConnectionStatus.kt

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@ import com.intellij.openapi.wm.StatusBarWidget
1212
import com.intellij.openapi.wm.StatusBarWidgetFactory
1313
import com.intellij.ui.awt.RelativePoint
1414
import com.intellij.util.Consumer
15-
import org.utbot.cpp.clion.plugin.actions.AskServerToGenerateJsonForProjectConfiguration
1615
import org.utbot.cpp.clion.plugin.actions.ReconnectAction
1716
import org.utbot.cpp.clion.plugin.actions.configure.ConfigureProjectAction
1817
import org.utbot.cpp.clion.plugin.actions.configure.ReconfigureProjectAction
1918
import org.utbot.cpp.clion.plugin.actions.ShowWizardAction
2019
import org.utbot.cpp.clion.plugin.listeners.ConnectionStatus
2120
import org.utbot.cpp.clion.plugin.listeners.UTBotEventsListener
21+
import org.utbot.cpp.clion.plugin.utils.projectLifetimeDisposable
2222
import java.awt.Component
2323
import java.awt.Point
2424
import java.awt.event.MouseEvent
@@ -50,14 +50,17 @@ class UTBotStatusBarWidget : StatusBarWidget, StatusBarWidget.TextPresentation {
5050

5151
override fun install(statusbar: StatusBar) {
5252
this.statusBar = statusbar
53-
statusbar.project?.messageBus?.connect()?.subscribe(
54-
UTBotEventsListener.CONNECTION_CHANGED_TOPIC,
55-
object : UTBotEventsListener {
56-
override fun onConnectionChange(oldStatus: ConnectionStatus, newStatus: ConnectionStatus) {
57-
myConnectionStatusText = newStatus.description
58-
statusBar?.updateWidget(ID())
59-
}
60-
})
53+
statusbar.project?.let { project ->
54+
// use project level service as disposable
55+
project.messageBus.connect(project.projectLifetimeDisposable).subscribe(
56+
UTBotEventsListener.CONNECTION_CHANGED_TOPIC,
57+
object : UTBotEventsListener {
58+
override fun onConnectionChange(oldStatus: ConnectionStatus, newStatus: ConnectionStatus) {
59+
myConnectionStatusText = newStatus.description
60+
statusBar?.updateWidget(ID())
61+
}
62+
})
63+
}
6164
}
6265

6366
override fun dispose() {}

clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/ui/statusBar/UTBotStatusBarVerboseWidget.kt

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import com.intellij.openapi.wm.StatusBarWidget
55
import com.intellij.util.Consumer
66
import org.utbot.cpp.clion.plugin.listeners.UTBotSettingsChangedListener
77
import org.utbot.cpp.clion.plugin.settings.settings
8+
import org.utbot.cpp.clion.plugin.utils.projectLifetimeDisposable
89
import java.awt.Component
910
import java.awt.event.MouseEvent
1011

@@ -16,9 +17,12 @@ class UTBotStatusBarVerboseWidget : StatusBarWidget, StatusBarWidget.TextPresent
1617
override fun install(statusbar: StatusBar) {
1718
this.statusBar = statusbar
1819
statusBar?.updateWidget(ID())
19-
statusbar.project?.messageBus?.connect()?.subscribe(UTBotSettingsChangedListener.TOPIC, UTBotSettingsChangedListener {
20-
statusbar.updateWidget(ID())
21-
})
20+
statusbar.project?.let { project ->
21+
project.messageBus.connect(project.projectLifetimeDisposable)
22+
.subscribe(UTBotSettingsChangedListener.TOPIC, UTBotSettingsChangedListener {
23+
statusbar.updateWidget(ID())
24+
})
25+
}
2226
}
2327

2428
override fun dispose() {}

clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/ui/targetsToolWindow/UTBotTargetsController.kt

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import org.utbot.cpp.clion.plugin.settings.settings
1212
import org.utbot.cpp.clion.plugin.utils.getCurrentClient
1313
import org.utbot.cpp.clion.plugin.utils.invokeOnEdt
1414
import org.utbot.cpp.clion.plugin.utils.logger
15+
import org.utbot.cpp.clion.plugin.utils.projectLifetimeDisposable
1516
import testsgen.Testgen
1617

1718
@Service
@@ -98,18 +99,17 @@ class UTBotTargetsController(val project: Project) {
9899
}
99100

100101
private fun connectToEvents() {
101-
project.messageBus.connect().also { connection ->
102+
// this connection is needed during project lifetime so we pass this service as parent disposable
103+
project.messageBus.connect(project.projectLifetimeDisposable).subscribe(
102104
// when we reconnected to server, the targets should be updated, so we request them from server
103-
connection.subscribe(
104-
UTBotEventsListener.CONNECTION_CHANGED_TOPIC,
105-
object : UTBotEventsListener {
106-
override fun onConnectionChange(oldStatus: ConnectionStatus, newStatus: ConnectionStatus) {
107-
if (newStatus != oldStatus) {
108-
requestTargetsFromServer()
109-
}
105+
UTBotEventsListener.CONNECTION_CHANGED_TOPIC,
106+
object : UTBotEventsListener {
107+
override fun onConnectionChange(oldStatus: ConnectionStatus, newStatus: ConnectionStatus) {
108+
if (newStatus != oldStatus) {
109+
requestTargetsFromServer()
110110
}
111111
}
112-
)
113-
}
112+
}
113+
)
114114
}
115115
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package org.utbot.cpp.clion.plugin.utils
2+
3+
import com.intellij.openapi.Disposable
4+
import com.intellij.openapi.components.Service
5+
import com.intellij.openapi.components.service
6+
import com.intellij.openapi.project.Project
7+
8+
/**
9+
* A project level service to be used as parent disposable
10+
* for disposables (objects that need to do some cleanup) with project lifetime
11+
*
12+
* Must not be used as a child disposable, because it is a child for [ClientManager] disposable
13+
*/
14+
@Service
15+
class ProjectLifetimeDisposable(project: Project) : Disposable {
16+
override fun dispose() {}
17+
}
18+
19+
val Project.projectLifetimeDisposable: ProjectLifetimeDisposable get() = this.service()

clion-plugin/src/test/kotlin/org/utbot/cpp/clion/plugin/BaseGenerationTestCase.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import com.intellij.testFramework.fixtures.CodeInsightTestFixture
88
import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory
99
import com.intellij.testFramework.fixtures.impl.TempDirTestFixtureImpl
1010
import com.intellij.util.io.delete
11-
import kotlinx.coroutines.runBlocking
1211
import org.junit.jupiter.api.AfterAll
1312
import org.junit.jupiter.api.AfterEach
1413
import org.junit.jupiter.api.TestInstance
@@ -98,10 +97,11 @@ abstract class BaseGenerationTestCase {
9897
/**
9998
* Waits until all requests initiated during tests are finished
10099
*/
101-
fun waitForRequestsToFinish() = runBlocking {
100+
fun waitForRequestsToFinish() {
102101
// requests to server are asynchronous, need to wait for server to respond
103102
client.waitForServerRequestsToFinish(ifNotFinished = { unfinishedCoroutines: List<Job> ->
104-
// some requests may be executed only on EDT, so we wk
103+
// some requests may be executed only on EDT, so we flush the queue. Otherwise,
104+
// these requests won't finish
105105
PlatformTestUtil.dispatchAllInvocationEventsInIdeEventQueue()
106106
logger.info("Waiting for requests to finish: $unfinishedCoroutines")
107107
})

0 commit comments

Comments
 (0)