Skip to content

Commit b71421c

Browse files
authored
add run all tests and show coverage action (#378)
* Merge coverage * Add action * small fix * small fix
1 parent f57a6da commit b71421c

File tree

9 files changed

+132
-37
lines changed

9 files changed

+132
-37
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package org.utbot.cpp.clion.plugin.actions.generate
2+
3+
import com.intellij.openapi.actionSystem.AnActionEvent
4+
import org.utbot.cpp.clion.plugin.client.requests.RunAllTestsWithCoverageRequest
5+
6+
class RunAllTestsWithCoverageAction: BaseGenerateTestsAction() {
7+
override fun actionPerformed(e: AnActionEvent) {
8+
RunAllTestsWithCoverageRequest(e).executeUsingCurrentClient()
9+
}
10+
11+
override fun isDefined(e: AnActionEvent): Boolean {
12+
return e.project != null
13+
}
14+
}

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

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@ import kotlinx.coroutines.Job
99
import kotlinx.coroutines.flow.Flow
1010
import kotlinx.coroutines.withContext
1111
import org.utbot.cpp.clion.plugin.actions.FocusAction
12+
import org.utbot.cpp.clion.plugin.coverage.Coverage
1213
import org.utbot.cpp.clion.plugin.coverage.UTBotCoverageEngine
1314
import org.utbot.cpp.clion.plugin.coverage.UTBotCoverageRunner
1415
import org.utbot.cpp.clion.plugin.coverage.UTBotCoverageSuite
1516
import org.utbot.cpp.clion.plugin.listeners.UTBotTestResultsReceivedListener
17+
import org.utbot.cpp.clion.plugin.utils.convertFromRemotePathIfNeeded
1618
import org.utbot.cpp.clion.plugin.utils.logger
1719
import org.utbot.cpp.clion.plugin.utils.notifyError
1820
import org.utbot.cpp.clion.plugin.utils.notifyInfo
@@ -45,6 +47,30 @@ class CoverageAndResultsHandler(
4547
notifyError(response.errorMessage, project)
4648
}
4749

50+
data class CoverageCollector(
51+
val fullyCovered: MutableSet<Int> = mutableSetOf(),
52+
val partiallyCovered: MutableSet<Int> = mutableSetOf(),
53+
val notCovered: MutableSet<Int> = mutableSetOf()
54+
) {
55+
fun toCoverage() = Coverage(fullyCovered, partiallyCovered, notCovered)
56+
}
57+
58+
val coverage = mutableMapOf<Path, CoverageCollector>()
59+
response.coveragesList.forEach { fileCoverageSimplified ->
60+
val local = fileCoverageSimplified.filePath.convertFromRemotePathIfNeeded(project).normalize()
61+
if (local !in coverage)
62+
coverage[local] = CoverageCollector()
63+
fileCoverageSimplified.fullCoverageLinesList.forEach { sourceLine ->
64+
coverage[local]?.fullyCovered?.add(sourceLine.line)
65+
}
66+
fileCoverageSimplified.partialCoverageLinesList.forEach { sourceLine ->
67+
coverage[local]?.partiallyCovered?.add(sourceLine.line)
68+
}
69+
fileCoverageSimplified.noCoverageLinesList.forEach { sourceLine ->
70+
coverage[local]?.notCovered?.add(sourceLine.line)
71+
}
72+
}
73+
4874
// when we received results, test statuses should be updated in the gutter
4975
project.messageBus.syncPublisher(UTBotTestResultsReceivedListener.TOPIC)
5076
.testResultsReceived(response.testRunResultsList)
@@ -54,20 +80,19 @@ class CoverageAndResultsHandler(
5480
val coverageRunner = CoverageRunner.getInstance(UTBotCoverageRunner::class.java)
5581
val manager = CoverageDataManager.getInstance(project)
5682
val suite = UTBotCoverageSuite(
83+
coverage.mapValues { it.value.toCoverage() },
5784
engine,
5885
response.coveragesList,
5986
coverageRunner = coverageRunner,
6087
name = "UTBot coverage suite",
61-
project = project
88+
project = project,
6289
)
6390

6491
manager.coverageGathered(suite)
6592
notifyCoverageReceived()
6693
}
6794

6895
private fun notifyCoverageReceived() {
69-
if (sourceFilePath != null) {
70-
notifyInfo("Coverage received!", project, FocusAction(sourceFilePath))
71-
}
96+
notifyInfo("Coverage received!", project, sourceFilePath?.let { FocusAction(it) })
7297
}
7398
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package org.utbot.cpp.clion.plugin.client.requests
2+
3+
import com.intellij.openapi.actionSystem.AnActionEvent
4+
import com.intellij.openapi.project.Project
5+
import kotlinx.coroutines.Job
6+
import kotlinx.coroutines.flow.Flow
7+
import org.utbot.cpp.clion.plugin.UTBot
8+
import org.utbot.cpp.clion.plugin.client.handlers.CoverageAndResultsHandler
9+
import org.utbot.cpp.clion.plugin.grpc.getRunWithCoverageRequestForAllTests
10+
import org.utbot.cpp.clion.plugin.utils.activeProject
11+
import testsgen.Testgen
12+
import testsgen.Testgen.CoverageAndResultsRequest
13+
import testsgen.TestsGenServiceGrpcKt
14+
15+
class RunAllTestsWithCoverageRequest(
16+
request: CoverageAndResultsRequest,
17+
project: Project,
18+
) : BaseRequest<CoverageAndResultsRequest, Flow<Testgen.CoverageAndResultsResponse>>(request, project) {
19+
20+
override val logMessage: String = "Sending request to get tests run results and coverage"
21+
22+
constructor(e: AnActionEvent) : this(getRunWithCoverageRequestForAllTests(e.activeProject()), e.activeProject())
23+
24+
override suspend fun Flow<Testgen.CoverageAndResultsResponse>.handle(cancellationJob: Job?) {
25+
if (cancellationJob?.isActive == true) {
26+
CoverageAndResultsHandler(
27+
project,
28+
this,
29+
UTBot.message("requests.coverage.description.progress"),
30+
cancellationJob,
31+
).handle()
32+
}
33+
}
34+
35+
override suspend fun TestsGenServiceGrpcKt.TestsGenServiceCoroutineStub.send(cancellationJob: Job?): Flow<Testgen.CoverageAndResultsResponse> =
36+
createTestsCoverageAndResult(request)
37+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package org.utbot.cpp.clion.plugin.coverage
2+
3+
data class Coverage(
4+
val fullyCovered: Set<Int> = setOf(),
5+
val partiallyCovered: Set<Int> = setOf(),
6+
val notCovered: Set<Int> = setOf()
7+
)

clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/coverage/UTBotCoverageRunner.kt

Lines changed: 27 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,11 @@ import com.intellij.rt.coverage.data.LineCoverage
88
import com.intellij.rt.coverage.data.LineData
99
import com.intellij.rt.coverage.data.ProjectData
1010
import com.intellij.util.io.exists
11-
import org.utbot.cpp.clion.plugin.utils.convertFromRemotePathIfNeeded
12-
import testsgen.Testgen
1311
import java.io.File
1412
import java.nio.charset.StandardCharsets
1513
import java.nio.file.Files
1614
import java.nio.file.Path
15+
import java.nio.file.Paths
1716

1817
/**
1918
* This class is used to convert from our representation of coverage to IntelliJ's [ProjectData]
@@ -31,42 +30,38 @@ class UTBotCoverageRunner : CoverageRunner() {
3130
*/
3231
override fun loadCoverageData(sessionDataFile: File, baseCoverageSuite: CoverageSuite?): ProjectData? {
3332
log.debug("loadCoverageData was called!")
34-
val coveragesList = (baseCoverageSuite as? UTBotCoverageSuite)?.coveragesList
35-
coveragesList ?: error("Coverage list is empty in loadCoverageData!")
33+
val fileToCoverageInfo: Map<Path, Coverage> =
34+
(baseCoverageSuite as? UTBotCoverageSuite)?.coverage ?: return null
3635
val projectData = ProjectData()
3736
var isAnyCoverage = false
38-
for (simplifiedCovInfo in coveragesList) {
39-
val filePathFromServer = simplifiedCovInfo.filePath
40-
if (filePathFromServer.isNotEmpty()) {
41-
isAnyCoverage = true
42-
val localFilePath = filePathFromServer.convertFromRemotePathIfNeeded(baseCoverageSuite.project)
43-
if (!localFilePath.exists()) {
44-
log.warn("Skipping $localFilePath in coverage processing as it does not exist!")
45-
continue
46-
}
47-
val linesCount = getLineCount(localFilePath)
48-
val lines = arrayOfNulls<LineData>(linesCount)
49-
val classData = projectData.getOrCreateClassData(provideQualifiedNameForFile(localFilePath.toString()))
50-
fun processRanges(rangesList: List<Testgen.SourceLine?>, status: Byte) {
51-
rangesList.filterNotNull().forEach { sourceLine ->
52-
val numberInFile = sourceLine.line - 1
53-
if (numberInFile >= linesCount) {
54-
log.warn("Skipping $localFilePath:${numberInFile} in coverage processing! Number of lines in file is $linesCount!")
55-
return@forEach
56-
}
57-
val lineData = LineData(sourceLine.line + 1, null)
58-
lineData.hits = status.toInt()
59-
lineData.setStatus(status)
60-
// todo: leave comments what is going on
61-
lines[numberInFile + 1] = lineData
62-
classData.registerMethodSignature(lineData)
37+
for ((file, coverage) in fileToCoverageInfo) {
38+
isAnyCoverage = true
39+
if (!file.exists()) {
40+
log.warn("Skipping $file in coverage processing as it does not exist!")
41+
continue
42+
}
43+
val linesCount = getLineCount(file)
44+
val lines = arrayOfNulls<LineData>(linesCount)
45+
val classData = projectData.getOrCreateClassData(provideQualifiedNameForFile(file.toString()))
46+
fun processLinesBatch(batch: Set<Int>, status: Byte) {
47+
// assuming: server's coverage lines indexes start from 1
48+
batch.forEach { lineIdx ->
49+
System.err.println("Processing idx : $lineIdx")
50+
if (lineIdx > linesCount) {
51+
log.warn("Skipping $file:${lineIdx} in coverage processing! Number of lines in file is $linesCount!")
52+
return@forEach
6353
}
54+
val lineData = LineData(lineIdx + 1, null)
55+
lineData.hits = status.toInt()
56+
lineData.setStatus(status)
57+
lines[lineIdx] = lineData
58+
classData.registerMethodSignature(lineData)
6459
}
65-
processRanges(simplifiedCovInfo.fullCoverageLinesList, LineCoverage.FULL)
66-
processRanges(simplifiedCovInfo.partialCoverageLinesList, LineCoverage.PARTIAL)
67-
processRanges(simplifiedCovInfo.noCoverageLinesList, LineCoverage.NONE)
6860
classData.setLines(lines)
6961
}
62+
processLinesBatch(coverage.fullyCovered, LineCoverage.FULL)
63+
processLinesBatch(coverage.partiallyCovered, LineCoverage.PARTIAL)
64+
processLinesBatch(coverage.notCovered, LineCoverage.NONE)
7065
}
7166
return if (isAnyCoverage) projectData else null
7267
}

clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/coverage/UTBotCoverageSuite.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import com.intellij.openapi.project.Project
88
import com.intellij.rt.coverage.data.ProjectData
99
import testsgen.Testgen
1010
import java.io.File
11+
import java.nio.file.Path
1112
import java.util.*
1213
import java.util.concurrent.TimeUnit
1314

@@ -18,6 +19,7 @@ import java.util.concurrent.TimeUnit
1819
* @param covLists - coverage information returned from server.
1920
*/
2021
class UTBotCoverageSuite(
22+
val coverage: Map<Path, Coverage> = emptyMap(),
2123
coverageEngine: UTBotCoverageEngine,
2224
covLists: List<Testgen.FileCoverageSimplified>? = null,
2325
name: String? = null,

clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/grpc/ActionsGrpcRequests.kt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,13 @@ fun getFolderGrpcRequest(e: AnActionEvent): Testgen.FolderRequest {
2121
.build()
2222
}
2323

24+
fun getRunWithCoverageRequestForAllTests(project: Project): Testgen.CoverageAndResultsRequest =
25+
Testgen.CoverageAndResultsRequest.newBuilder()
26+
.setCoverage(true)
27+
.setSettingsContext(getSettingsContextMessage(project))
28+
.setProjectContext(getProjectContextMessage(project))
29+
.build()
30+
2431
fun getFileGrpcRequest(e: AnActionEvent): Testgen.FileRequest {
2532
val project = e.activeProject()
2633
val filePath = e.getRequiredData(CommonDataKeys.VIRTUAL_FILE).path
@@ -81,7 +88,11 @@ fun getPredicateGrpcRequest(
8188
.build()
8289
}
8390

84-
private fun getPredicateGrpcRequest(predicate: String, returnValue: String, type: Util.ValidationType): Util.PredicateInfo =
91+
private fun getPredicateGrpcRequest(
92+
predicate: String,
93+
returnValue: String,
94+
type: Util.ValidationType
95+
): Util.PredicateInfo =
8596
Util.PredicateInfo.newBuilder()
8697
.setPredicate(predicate)
8798
.setReturnValue(returnValue)

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ class UTBotTargetsController(val project: Project) {
4747
project,
4848
getProjectTargetsGrpcRequest(project),
4949
processTargets = { targetsResponse: Testgen.ProjectTargetsResponse ->
50+
System.err.println("Received targets: $targetsResponse")
5051
invokeOnEdt {
5152
targetsToolWindow.setBusy(false)
5253

clion-plugin/src/main/resources/META-INF/plugin.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,5 +177,8 @@
177177
description="Generates tests for file without context of a project">
178178
<synonym text="US"/>
179179
</action>
180+
<action id="org.utbot.cpp.clion.plugin.actions.generate.RunAllTestsWithCoverageAction"
181+
class="org.utbot.cpp.clion.plugin.actions.generate.RunAllTestsWithCoverageAction"
182+
text="UTBot: Run All Tests and Show Coverage"/>
180183
</actions>
181184
</idea-plugin>

0 commit comments

Comments
 (0)