Skip to content

Commit f57a6da

Browse files
vol0nEgorkaKulikov
andauthored
backup sarif report (#379)
* Backup sarif report * backup sarif report * Little code improvements * handle one sarif report Co-authored-by: Egor Kulikov <[email protected]>
1 parent d94dbaa commit f57a6da

File tree

7 files changed

+134
-69
lines changed

7 files changed

+134
-69
lines changed

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import org.utbot.cpp.clion.plugin.utils.notifyError
1515
import org.utbot.cpp.clion.plugin.utils.notifyInfo
1616
import org.utbot.cpp.clion.plugin.utils.notifyUnknownResponse
1717
import org.utbot.cpp.clion.plugin.utils.notifyWarning
18-
import org.utbot.cpp.clion.plugin.utils.refreshAndFindNioFile
18+
import org.utbot.cpp.clion.plugin.utils.markDirtyAndRefresh
1919
import testsgen.Testgen
2020

2121
abstract class ProjectConfigResponseHandler(
@@ -98,7 +98,7 @@ class CreateBuildDirHandler(
9898
}
9999
else -> notifyUnknownResponse(response, project)
100100
}
101-
refreshAndFindNioFile(project.settings.buildDirPath)
101+
markDirtyAndRefresh(project.settings.buildDirPath)
102102
}
103103
}
104104

@@ -117,6 +117,6 @@ class GenerateJsonHandler(
117117
)
118118
else -> notifyUnknownResponse(response, project)
119119
}
120-
refreshAndFindNioFile(project.settings.buildDirPath)
120+
markDirtyAndRefresh(project.settings.buildDirPath)
121121
}
122122
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package org.utbot.cpp.clion.plugin.client.handlers
2+
3+
import com.intellij.openapi.project.Project
4+
import org.utbot.cpp.clion.plugin.utils.convertFromRemotePathIfNeeded
5+
import testsgen.Util
6+
import java.nio.file.Path
7+
8+
class SourceCode private constructor(
9+
val localPath: Path,
10+
val remotePath: String,
11+
val content: String,
12+
val regressionMethodsNumber: Int,
13+
val errorMethodsNumber: Int
14+
) {
15+
constructor(serverSourceCode: Util.SourceCode, project: Project) : this(
16+
serverSourceCode.filePath.convertFromRemotePathIfNeeded(project),
17+
serverSourceCode.filePath,
18+
serverSourceCode.code,
19+
serverSourceCode.regressionMethodsNumber,
20+
serverSourceCode.errorMethodsNumber
21+
)
22+
}

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

Lines changed: 87 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,123 @@
11
package org.utbot.cpp.clion.plugin.client.handlers
22

3+
import com.intellij.openapi.components.service
34
import com.intellij.openapi.project.Project
5+
import com.intellij.util.io.exists
46
import kotlinx.coroutines.Job
57
import kotlinx.coroutines.flow.Flow
8+
import org.utbot.cpp.clion.plugin.settings.settings
9+
import org.utbot.cpp.clion.plugin.ui.services.TestsResultsStorage
610
import org.utbot.cpp.clion.plugin.utils.convertFromRemotePathIfNeeded
711
import org.utbot.cpp.clion.plugin.utils.createFileWithText
12+
import org.utbot.cpp.clion.plugin.utils.isSarifReport
813
import org.utbot.cpp.clion.plugin.utils.logger
9-
import org.utbot.cpp.clion.plugin.utils.refreshAndFindNioFile
14+
import org.utbot.cpp.clion.plugin.utils.markDirtyAndRefresh
15+
import org.utbot.cpp.clion.plugin.utils.nioPath
1016
import testsgen.Testgen
1117
import testsgen.Util
18+
import java.nio.file.Files
1219
import java.nio.file.Path
20+
import java.nio.file.Paths
21+
import java.time.ZoneId
1322

1423
class TestsStreamHandler(
1524
project: Project,
1625
grpcStream: Flow<Testgen.TestsResponse>,
1726
progressName: String,
1827
cancellationJob: Job,
19-
private val onSuccess: (List<Path>)->Unit = {},
20-
private val onError: (Throwable)->Unit = {}
21-
): StreamHandlerWithProgress<Testgen.TestsResponse>(project, grpcStream, progressName, cancellationJob) {
28+
private val onSuccess: (List<Path>) -> Unit = {},
29+
private val onError: (Throwable) -> Unit = {}
30+
) : StreamHandlerWithProgress<Testgen.TestsResponse>(project, grpcStream, progressName, cancellationJob) {
31+
2232
private val myGeneratedTestFilesLocalFS: MutableList<Path> = mutableListOf()
2333

2434
override fun onData(data: Testgen.TestsResponse) {
2535
super.onData(data)
26-
handleSourceCode(data.testSourcesList)
27-
if (data.hasStubs()) {
28-
handleSourceCode(data.stubs.stubSourcesList, true)
29-
}
36+
37+
val testSourceCodes = data.testSourcesList
38+
.map { SourceCode(it, project) }
39+
.filter { !it.localPath.isSarifReport() }
40+
handleTestSources(testSourceCodes)
41+
42+
val stubSourceCodes = data.stubs.stubSourcesList.map { SourceCode(it, project) }
43+
handleStubSources(stubSourceCodes)
44+
45+
val sarifReport =
46+
data.testSourcesList.find { it.filePath.convertFromRemotePathIfNeeded(project).isSarifReport() }?.let {
47+
SourceCode(it, project)
48+
}
49+
sarifReport?.let { handleSarifReport(it) }
50+
51+
// for new generated tests remove previous testResults
52+
project.service<TestsResultsStorage>().clearTestResults(testSourceCodes)
53+
54+
// tell ide to refresh vfs and refresh project tree
55+
markDirtyAndRefresh(project.nioPath)
3056
}
3157

32-
override fun Testgen.TestsResponse.getProgress(): Util.Progress {
33-
return progress
58+
private fun handleSarifReport(sarif: SourceCode) {
59+
backupPreviousClientSarifReport(sarif.localPath)
60+
createSourceCodeFiles(listOf(sarif), "sarif report")
61+
project.logger.info { "Generated SARIF report file ${sarif.localPath}" }
3462
}
3563

36-
private fun handleSourceCode(sources: List<Util.SourceCode>, isStubs: Boolean = false) {
37-
sources.forEach { sourceCode ->
38-
val filePath: Path = sourceCode.filePath.convertFromRemotePathIfNeeded(project)
64+
private fun handleTestSources(sources: List<SourceCode>) {
65+
if (project.settings.isRemoteScenario) {
66+
createSourceCodeFiles(sources, "test")
67+
}
3968

40-
if (!isStubs)
41-
myGeneratedTestFilesLocalFS.add(filePath)
69+
// prepare list of generated test files for further processing
70+
myGeneratedTestFilesLocalFS.addAll(sources.map { it.localPath })
4271

43-
if (sourceCode.code.isNotEmpty()) {
44-
project.logger.trace { "Creating generated test file: $filePath." }
45-
createFileWithText(
46-
filePath,
47-
sourceCode.code
48-
)
72+
sources.forEach { sourceCode ->
73+
val isTestSourceFile = sourceCode.localPath.endsWith("_test.cpp")
74+
val testsGenerationResultMessage = if (isTestSourceFile) {
75+
"Generated ${sourceCode.regressionMethodsNumber} tests in regression suite" +
76+
" and ${sourceCode.errorMethodsNumber} tests in error suite"
77+
} else {
78+
// .h file
79+
"Generated test file ${sourceCode.localPath}"
4980
}
81+
logger.info(testsGenerationResultMessage)
82+
}
83+
}
5084

51-
var infoMessage = "Generated " + if (isStubs) "stub" else "test" + " file"
52-
if (isGeneratedFileTestSourceFile(filePath.toString()))
53-
infoMessage += " with ${sourceCode.regressionMethodsNumber} tests in regression suite" +
54-
" and ${sourceCode.errorMethodsNumber} tests in error suite"
55-
project.logger.info { "$infoMessage: $filePath" }
85+
private fun handleStubSources(sources: List<SourceCode>) {
86+
if (project.settings.isRemoteScenario) {
87+
createSourceCodeFiles(sources, "stub")
88+
}
89+
}
5690

57-
refreshAndFindNioFile(filePath)
91+
private fun createSourceCodeFiles(sourceCodes: List<SourceCode>, fileKind: String) {
92+
sourceCodes.forEach {
93+
project.logger.info { "Write $fileKind file ${it.remotePath} to ${it.localPath}" }
94+
createFileWithText(it.localPath, it.content)
5895
}
5996
}
6097

61-
private fun isGeneratedFileTestSourceFile(fileName: String) = fileName.endsWith("_test.cpp")
98+
override fun Testgen.TestsResponse.getProgress(): Util.Progress = progress
99+
100+
private fun backupPreviousClientSarifReport(previousReportPaths: Path) {
101+
fun Number.pad2(): String = ("0$this").takeLast(2)
102+
103+
if (previousReportPaths.exists()) {
104+
val ctime = Files.getLastModifiedTime(previousReportPaths)
105+
.toInstant()
106+
.atZone(ZoneId.systemDefault())
107+
.toLocalDateTime()
108+
109+
val newReportName = "project_code_analysis-" +
110+
ctime.year.toString() +
111+
(ctime.monthValue + 1).pad2() +
112+
ctime.dayOfMonth.pad2() +
113+
ctime.hour.pad2() +
114+
ctime.minute.pad2() +
115+
ctime.second.pad2() +
116+
".sarif"
117+
val newPath = Paths.get(previousReportPaths.parent.toString(), newReportName)
118+
Files.move(previousReportPaths, newPath)
119+
}
120+
}
62121

63122
override fun onCompletion(exception: Throwable?) {
64123
super.onCompletion(exception)

clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/client/requests/test/BaseTestsRequest.kt

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,10 @@ abstract class BaseTestsRequest<R>(request: R, project: Project, private val pro
3535
}
3636
}
3737

38-
open fun getFocusTarget(generatedTestFiles: List<Path>): Path? {
39-
return generatedTestFiles.filter { !isHeaderFile(it) && !isSarifReport(it) }.getLongestCommonPathFromRoot()
40-
}
41-
42-
override fun logRequest() {
43-
logger.info { "$logMessage \n$request" }
44-
}
38+
open fun getFocusTarget(generatedTestFiles: List<Path>): Path? =
39+
generatedTestFiles
40+
.filter { !isHeaderFile(it) && !it.isSarifReport() }
41+
.getLongestCommonPathFromRoot()
4542

4643
open fun getInfoMessage() = "Tests generated!"
4744

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

Lines changed: 9 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,7 @@ import com.intellij.openapi.components.Service
55
import com.intellij.openapi.diagnostic.Logger
66
import com.intellij.openapi.fileEditor.FileEditorManager
77
import com.intellij.openapi.project.Project
8-
import com.intellij.openapi.vfs.VirtualFileManager
9-
import com.intellij.openapi.vfs.newvfs.BulkFileListener
10-
import com.intellij.openapi.vfs.newvfs.events.VFileEvent
8+
import org.utbot.cpp.clion.plugin.client.handlers.SourceCode
119
import org.utbot.cpp.clion.plugin.listeners.UTBotTestResultsReceivedListener
1210
import org.utbot.cpp.clion.plugin.utils.convertFromRemotePathIfNeeded
1311
import testsgen.Testgen
@@ -31,31 +29,18 @@ class TestsResultsStorage(val project: Project) {
3129

3230
forceGutterIconsUpdate()
3331
})
34-
35-
connection.subscribe(VirtualFileManager.VFS_CHANGES, object : BulkFileListener {
36-
override fun after(events: MutableList<out VFileEvent>) {
37-
var wasSave = false
38-
events.forEach { event ->
39-
if (event.isFromSave) {
40-
wasSave = true
41-
storage.forEach { entry ->
42-
if (entry.value.testFilePath != event.path) {
43-
storage.remove(entry.key)
44-
}
45-
}
46-
}
47-
}
48-
49-
if (wasSave) {
50-
forceGutterIconsUpdate()
51-
}
52-
}
53-
})
54-
5532
}
5633

5734
fun getTestResultByTestName(testName: String): Testgen.TestResultObject? = storage[testName]
5835

36+
/**
37+
* Cleans the results of previous test run if tests were regenerated.
38+
*/
39+
fun clearTestResults(sourceCodes: List<SourceCode>) {
40+
val localFilePaths = sourceCodes.map { it.localPath }.toSet()
41+
storage.values.removeIf { it.testFilePath.convertFromRemotePathIfNeeded(project) in localFilePaths }
42+
}
43+
5944
private fun shouldForceUpdate(): Boolean {
6045
val currentlyOpenedFilePaths = FileEditorManager.getInstance(project)
6146
.selectedEditors

clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/utils/FileUtils.kt

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,12 @@ import com.intellij.util.io.exists
66
import kotlin.io.path.writeText
77
import java.nio.file.Path
88

9-
fun refreshAndFindNioFile(path: Path, async: Boolean = true, recursive: Boolean = true, reloadChildren: Boolean = true) {
10-
VfsUtil.markDirtyAndRefresh(async, recursive, reloadChildren, path.toFile())
11-
}
9+
fun markDirtyAndRefresh(
10+
path: Path,
11+
async: Boolean = true,
12+
recursive: Boolean = true,
13+
reloadChildren: Boolean = true,
14+
) = VfsUtil.markDirtyAndRefresh(async, recursive, reloadChildren, path.toFile())
1215

1316
fun createFileWithText(filePath: Path, text: String) {
1417
with(filePath) {
@@ -24,7 +27,3 @@ fun isCPPFileName(fileName: String) = """.*\.(cpp|hpp|h)""".toRegex().matches(fi
2427

2528
fun isHeaderFile(fileName: String) = """.*\.([ch])""".toRegex().matches(fileName)
2629
fun isHeaderFile(path: Path) = isHeaderFile(path.fileName.toString())
27-
28-
fun isSarifReport(fileName: String) = fileName.endsWith(".sarif")
29-
30-
fun isSarifReport(path: Path) = isSarifReport(path.fileName.toString())

clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/utils/PathUtils.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import java.util.*
1515
import kotlin.io.path.div
1616

1717
val Project.path get() = this.basePath ?: error("Project path can't be null!")
18+
val Project.nioPath: Path get() = Paths.get(this.path)
1819

1920
fun relativize(from: String, to: String): String {
2021
val toPath = Paths.get(to)
@@ -51,6 +52,8 @@ fun Path.visitAllDirectories(action: (Path) -> Unit) {
5152
}
5253
}
5354

55+
fun Path.isSarifReport() = this.fileName.toString().endsWith(".sarif")
56+
5457
fun String.fileNameOrNull(): String? {
5558
return try {
5659
Paths.get(this).fileName.toString()

0 commit comments

Comments
 (0)