1
- package org.utbot.cpp.clion.plugin.client.handlers
1
+ package org.utbot.cpp.clion.plugin.client.handlers.testsStreamHandler
2
2
3
3
import com.intellij.openapi.components.service
4
+ import com.intellij.openapi.progress.ProgressIndicator
5
+ import com.intellij.openapi.progress.ProgressManager
6
+ import com.intellij.openapi.progress.Task
4
7
import com.intellij.openapi.project.Project
5
8
import com.intellij.util.io.exists
9
+ import com.intellij.util.io.readText
10
+ import kotlin.io.path.appendText
6
11
import kotlinx.coroutines.CancellationException
7
12
import kotlinx.coroutines.Job
8
13
import kotlinx.coroutines.flow.Flow
14
+ import org.utbot.cpp.clion.plugin.UTBot
15
+ import org.utbot.cpp.clion.plugin.client.handlers.SourceCode
16
+ import org.utbot.cpp.clion.plugin.client.handlers.StreamHandlerWithProgress
9
17
import org.utbot.cpp.clion.plugin.settings.settings
10
18
import org.utbot.cpp.clion.plugin.ui.services.TestsResultsStorage
11
- import org.utbot.cpp.clion.plugin.utils.convertFromRemotePathIfNeeded
12
19
import org.utbot.cpp.clion.plugin.utils.createFileWithText
13
20
import org.utbot.cpp.clion.plugin.utils.invokeOnEdt
21
+ import org.utbot.cpp.clion.plugin.utils.isCMakeListsFile
14
22
import org.utbot.cpp.clion.plugin.utils.isSarifReport
15
23
import org.utbot.cpp.clion.plugin.utils.logger
16
24
import org.utbot.cpp.clion.plugin.utils.markDirtyAndRefresh
17
25
import org.utbot.cpp.clion.plugin.utils.nioPath
26
+ import org.utbot.cpp.clion.plugin.utils.notifyError
18
27
import testsgen.Testgen
19
28
import testsgen.Util
29
+ import java.io.IOException
20
30
import java.nio.file.Files
21
31
import java.nio.file.Path
22
32
import java.nio.file.Paths
@@ -31,34 +41,91 @@ class TestsStreamHandler(
31
41
) : StreamHandlerWithProgress<Testgen.TestsResponse>(project, grpcStream, progressName, cancellationJob) {
32
42
33
43
private val myGeneratedTestFilesLocalFS: MutableList <Path > = mutableListOf ()
44
+ private var isCMakePresent = false
45
+ private var isSarifPresent = false
34
46
35
47
override fun onData (data : Testgen .TestsResponse ) {
36
48
super .onData(data)
37
49
38
- val testSourceCodes = data.testSourcesList
39
- .map { SourceCode (it, project) }
40
- .filter { ! it.localPath.isSarifReport() }
50
+ // currently testSourcesList contains not only test sourse codes but
51
+ // also some extra files like sarif report, cmake generated file
52
+ // this was done for compatibility
53
+ val sourceCodes = data.testSourcesList.mapNotNull { it.toSourceCodeOrNull() }
54
+ val testSourceCodes = sourceCodes
55
+ .filter { ! it.localPath.isSarifReport() && ! it.localPath.isCMakeListsFile() }
41
56
handleTestSources(testSourceCodes)
42
57
43
- val stubSourceCodes = data.stubs.stubSourcesList.map { SourceCode (it, project) }
58
+ // for stubs we know that stubSourcesList contains only stub sources
59
+ val stubSourceCodes = data.stubs.stubSourcesList.mapNotNull { it.toSourceCodeOrNull() }
44
60
handleStubSources(stubSourceCodes)
45
61
46
- val sarifReport =
47
- data.testSourcesList.find { it.filePath.convertFromRemotePathIfNeeded(project).isSarifReport() }?.let {
48
- SourceCode (it, project)
49
- }
50
- sarifReport?.let { handleSarifReport(it) }
62
+ val sarifReport = sourceCodes.find { it.localPath.isSarifReport() }
63
+ if (sarifReport != null )
64
+ handleSarifReport(sarifReport)
65
+
66
+ val cmakeFile = sourceCodes.find { it.localPath.endsWith(" CMakeLists.txt" ) }
67
+ if (cmakeFile != null )
68
+ handleCMakeFile(cmakeFile)
51
69
52
70
// for new generated tests remove previous testResults
53
71
project.service<TestsResultsStorage >().clearTestResults(testSourceCodes)
54
72
}
55
73
56
74
override fun onFinish () {
57
75
super .onFinish()
76
+ if (! isCMakePresent)
77
+ project.logger.warn(" CMake file is missing in the tests response" )
78
+ if (! isSarifPresent)
79
+ project.logger.warn(" Sarif report is missing in the tests response" )
58
80
// tell ide to refresh vfs and refresh project tree
59
81
markDirtyAndRefresh(project.nioPath)
60
82
}
61
83
84
+ private fun handleCMakeFile (cmakeSourceCode : SourceCode ) {
85
+ isCMakePresent = true
86
+ createFileWithText(cmakeSourceCode.localPath, cmakeSourceCode.content)
87
+ val rootCMakeFile = project.nioPath.resolve(" CMakeLists.txt" )
88
+ if (! rootCMakeFile.exists()) {
89
+ project.logger.warn(" Root CMakeLists.txt file does not exist. Skipping CMake patches." )
90
+ return
91
+ }
92
+
93
+ val currentCMakeFileContent = rootCMakeFile.readText()
94
+ val cMakePrinter = CMakePrinter (currentCMakeFileContent)
95
+ invokeOnEdt { // we can show dialog only from edt
96
+
97
+ if (! project.settings.storedSettings.isGTestInstalled) {
98
+ val shouldInstallGTestDialog = ShouldInstallGTestDialog (project)
99
+
100
+ if (shouldInstallGTestDialog.showAndGet()) {
101
+ cMakePrinter.addDownloadGTestSection()
102
+ }
103
+
104
+ // whether user confirmed that gtest is installed or we added the gtest section, from now on
105
+ // we will assume that gtest is installed
106
+ project.settings.storedSettings.isGTestInstalled = true
107
+ }
108
+
109
+ cMakePrinter.addSubdirectory(project.settings.storedSettings.testDirRelativePath)
110
+
111
+ // currently we are on EDT, but writing to file better to be done on background thread
112
+ ProgressManager .getInstance().run (object : Task .Backgroundable (project, " Modifying CMakeLists.txt file" , false ) {
113
+ override fun run (progressIndicator : ProgressIndicator ) {
114
+ try {
115
+ if (! cMakePrinter.isEmpty)
116
+ project.nioPath.resolve(" CMakeLists.txt" ).appendText(cMakePrinter.get())
117
+ } catch (e: IOException ) {
118
+ notifyError(
119
+ UTBot .message(" notify.title.error" ),
120
+ UTBot .message(" notify.error.write.to.file" , e.message ? : " unknown reason" ),
121
+ project
122
+ )
123
+ }
124
+ }
125
+ })
126
+ }
127
+ }
128
+
62
129
override fun onCompletion (exception : Throwable ? ) {
63
130
invokeOnEdt {
64
131
indicator.stopShowingProgressInUI()
@@ -71,6 +138,7 @@ class TestsStreamHandler(
71
138
}
72
139
73
140
private fun handleSarifReport (sarif : SourceCode ) {
141
+ isSarifPresent = true
74
142
backupPreviousClientSarifReport(sarif.localPath)
75
143
createSourceCodeFiles(listOf (sarif), " sarif report" )
76
144
project.logger.info { " Generated SARIF report file ${sarif.localPath} " }
@@ -97,6 +165,15 @@ class TestsStreamHandler(
97
165
}
98
166
}
99
167
168
+ private fun Util.SourceCode.toSourceCodeOrNull (): SourceCode ? {
169
+ return try {
170
+ SourceCode (this , project)
171
+ } catch (e: IllegalArgumentException ) {
172
+ project.logger.error(" Could not convert remote path to local version: bad path: ${this .filePath} " )
173
+ null
174
+ }
175
+ }
176
+
100
177
private fun handleStubSources (sources : List <SourceCode >) {
101
178
if (project.settings.isRemoteScenario) {
102
179
createSourceCodeFiles(sources, " stub" )
0 commit comments