Skip to content

add run all tests and show coverage action #378

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 5 commits into from
Aug 11, 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
@@ -0,0 +1,14 @@
package org.utbot.cpp.clion.plugin.actions.generate

import com.intellij.openapi.actionSystem.AnActionEvent
import org.utbot.cpp.clion.plugin.client.requests.RunAllTestsWithCoverageRequest

class RunAllTestsWithCoverageAction: BaseGenerateTestsAction() {
override fun actionPerformed(e: AnActionEvent) {
RunAllTestsWithCoverageRequest(e).executeUsingCurrentClient()
}

override fun isDefined(e: AnActionEvent): Boolean {
return e.project != null
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.withContext
import org.utbot.cpp.clion.plugin.actions.FocusAction
import org.utbot.cpp.clion.plugin.coverage.Coverage
import org.utbot.cpp.clion.plugin.coverage.UTBotCoverageEngine
import org.utbot.cpp.clion.plugin.coverage.UTBotCoverageRunner
import org.utbot.cpp.clion.plugin.coverage.UTBotCoverageSuite
import org.utbot.cpp.clion.plugin.listeners.UTBotTestResultsReceivedListener
import org.utbot.cpp.clion.plugin.utils.convertFromRemotePathIfNeeded
import org.utbot.cpp.clion.plugin.utils.logger
import org.utbot.cpp.clion.plugin.utils.notifyError
import org.utbot.cpp.clion.plugin.utils.notifyInfo
Expand Down Expand Up @@ -45,6 +47,30 @@ class CoverageAndResultsHandler(
notifyError(response.errorMessage, project)
}

data class CoverageCollector(
val fullyCovered: MutableSet<Int> = mutableSetOf(),
val partiallyCovered: MutableSet<Int> = mutableSetOf(),
val notCovered: MutableSet<Int> = mutableSetOf()
) {
fun toCoverage() = Coverage(fullyCovered, partiallyCovered, notCovered)
}

val coverage = mutableMapOf<Path, CoverageCollector>()
response.coveragesList.forEach { fileCoverageSimplified ->
val local = fileCoverageSimplified.filePath.convertFromRemotePathIfNeeded(project).normalize()
if (local !in coverage)
coverage[local] = CoverageCollector()
fileCoverageSimplified.fullCoverageLinesList.forEach { sourceLine ->
coverage[local]?.fullyCovered?.add(sourceLine.line)
}
fileCoverageSimplified.partialCoverageLinesList.forEach { sourceLine ->
coverage[local]?.partiallyCovered?.add(sourceLine.line)
}
fileCoverageSimplified.noCoverageLinesList.forEach { sourceLine ->
coverage[local]?.notCovered?.add(sourceLine.line)
}
}

// when we received results, test statuses should be updated in the gutter
project.messageBus.syncPublisher(UTBotTestResultsReceivedListener.TOPIC)
.testResultsReceived(response.testRunResultsList)
Expand All @@ -54,20 +80,19 @@ class CoverageAndResultsHandler(
val coverageRunner = CoverageRunner.getInstance(UTBotCoverageRunner::class.java)
val manager = CoverageDataManager.getInstance(project)
val suite = UTBotCoverageSuite(
coverage.mapValues { it.value.toCoverage() },
engine,
response.coveragesList,
coverageRunner = coverageRunner,
name = "UTBot coverage suite",
project = project
project = project,
)

manager.coverageGathered(suite)
notifyCoverageReceived()
}

private fun notifyCoverageReceived() {
if (sourceFilePath != null) {
notifyInfo("Coverage received!", project, FocusAction(sourceFilePath))
}
notifyInfo("Coverage received!", project, sourceFilePath?.let { FocusAction(it) })
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package org.utbot.cpp.clion.plugin.client.requests

import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.project.Project
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow
import org.utbot.cpp.clion.plugin.UTBot
import org.utbot.cpp.clion.plugin.client.handlers.CoverageAndResultsHandler
import org.utbot.cpp.clion.plugin.grpc.getRunWithCoverageRequestForAllTests
import org.utbot.cpp.clion.plugin.utils.activeProject
import testsgen.Testgen
import testsgen.Testgen.CoverageAndResultsRequest
import testsgen.TestsGenServiceGrpcKt

class RunAllTestsWithCoverageRequest(
request: CoverageAndResultsRequest,
project: Project,
) : BaseRequest<CoverageAndResultsRequest, Flow<Testgen.CoverageAndResultsResponse>>(request, project) {

override val logMessage: String = "Sending request to get tests run results and coverage"

constructor(e: AnActionEvent) : this(getRunWithCoverageRequestForAllTests(e.activeProject()), e.activeProject())

override suspend fun Flow<Testgen.CoverageAndResultsResponse>.handle(cancellationJob: Job?) {
if (cancellationJob?.isActive == true) {
CoverageAndResultsHandler(
project,
this,
UTBot.message("requests.coverage.description.progress"),
cancellationJob,
).handle()
}
}

override suspend fun TestsGenServiceGrpcKt.TestsGenServiceCoroutineStub.send(cancellationJob: Job?): Flow<Testgen.CoverageAndResultsResponse> =
createTestsCoverageAndResult(request)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.utbot.cpp.clion.plugin.coverage

data class Coverage(
val fullyCovered: Set<Int> = setOf(),
val partiallyCovered: Set<Int> = setOf(),
val notCovered: Set<Int> = setOf()
)
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,11 @@ import com.intellij.rt.coverage.data.LineCoverage
import com.intellij.rt.coverage.data.LineData
import com.intellij.rt.coverage.data.ProjectData
import com.intellij.util.io.exists
import org.utbot.cpp.clion.plugin.utils.convertFromRemotePathIfNeeded
import testsgen.Testgen
import java.io.File
import java.nio.charset.StandardCharsets
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths

/**
* This class is used to convert from our representation of coverage to IntelliJ's [ProjectData]
Expand All @@ -31,42 +30,38 @@ class UTBotCoverageRunner : CoverageRunner() {
*/
override fun loadCoverageData(sessionDataFile: File, baseCoverageSuite: CoverageSuite?): ProjectData? {
log.debug("loadCoverageData was called!")
val coveragesList = (baseCoverageSuite as? UTBotCoverageSuite)?.coveragesList
coveragesList ?: error("Coverage list is empty in loadCoverageData!")
val fileToCoverageInfo: Map<Path, Coverage> =
(baseCoverageSuite as? UTBotCoverageSuite)?.coverage ?: return null
val projectData = ProjectData()
var isAnyCoverage = false
for (simplifiedCovInfo in coveragesList) {
val filePathFromServer = simplifiedCovInfo.filePath
if (filePathFromServer.isNotEmpty()) {
isAnyCoverage = true
val localFilePath = filePathFromServer.convertFromRemotePathIfNeeded(baseCoverageSuite.project)
if (!localFilePath.exists()) {
log.warn("Skipping $localFilePath in coverage processing as it does not exist!")
continue
}
val linesCount = getLineCount(localFilePath)
val lines = arrayOfNulls<LineData>(linesCount)
val classData = projectData.getOrCreateClassData(provideQualifiedNameForFile(localFilePath.toString()))
fun processRanges(rangesList: List<Testgen.SourceLine?>, status: Byte) {
rangesList.filterNotNull().forEach { sourceLine ->
val numberInFile = sourceLine.line - 1
if (numberInFile >= linesCount) {
log.warn("Skipping $localFilePath:${numberInFile} in coverage processing! Number of lines in file is $linesCount!")
return@forEach
}
val lineData = LineData(sourceLine.line + 1, null)
lineData.hits = status.toInt()
lineData.setStatus(status)
// todo: leave comments what is going on
lines[numberInFile + 1] = lineData
classData.registerMethodSignature(lineData)
for ((file, coverage) in fileToCoverageInfo) {
isAnyCoverage = true
if (!file.exists()) {
log.warn("Skipping $file in coverage processing as it does not exist!")
continue
}
val linesCount = getLineCount(file)
val lines = arrayOfNulls<LineData>(linesCount)
val classData = projectData.getOrCreateClassData(provideQualifiedNameForFile(file.toString()))
fun processLinesBatch(batch: Set<Int>, status: Byte) {
// assuming: server's coverage lines indexes start from 1
batch.forEach { lineIdx ->
System.err.println("Processing idx : $lineIdx")
if (lineIdx > linesCount) {
log.warn("Skipping $file:${lineIdx} in coverage processing! Number of lines in file is $linesCount!")
return@forEach
}
val lineData = LineData(lineIdx + 1, null)
lineData.hits = status.toInt()
lineData.setStatus(status)
lines[lineIdx] = lineData
classData.registerMethodSignature(lineData)
}
processRanges(simplifiedCovInfo.fullCoverageLinesList, LineCoverage.FULL)
processRanges(simplifiedCovInfo.partialCoverageLinesList, LineCoverage.PARTIAL)
processRanges(simplifiedCovInfo.noCoverageLinesList, LineCoverage.NONE)
classData.setLines(lines)
}
processLinesBatch(coverage.fullyCovered, LineCoverage.FULL)
processLinesBatch(coverage.partiallyCovered, LineCoverage.PARTIAL)
processLinesBatch(coverage.notCovered, LineCoverage.NONE)
}
return if (isAnyCoverage) projectData else null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import com.intellij.openapi.project.Project
import com.intellij.rt.coverage.data.ProjectData
import testsgen.Testgen
import java.io.File
import java.nio.file.Path
import java.util.*
import java.util.concurrent.TimeUnit

Expand All @@ -18,6 +19,7 @@ import java.util.concurrent.TimeUnit
* @param covLists - coverage information returned from server.
*/
class UTBotCoverageSuite(
val coverage: Map<Path, Coverage> = emptyMap(),
coverageEngine: UTBotCoverageEngine,
covLists: List<Testgen.FileCoverageSimplified>? = null,
name: String? = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ fun getFolderGrpcRequest(e: AnActionEvent): Testgen.FolderRequest {
.build()
}

fun getRunWithCoverageRequestForAllTests(project: Project): Testgen.CoverageAndResultsRequest =
Testgen.CoverageAndResultsRequest.newBuilder()
.setCoverage(true)
.setSettingsContext(getSettingsContextMessage(project))
.setProjectContext(getProjectContextMessage(project))
.build()

fun getFileGrpcRequest(e: AnActionEvent): Testgen.FileRequest {
val project = e.activeProject()
val filePath = e.getRequiredData(CommonDataKeys.VIRTUAL_FILE).path
Expand Down Expand Up @@ -81,7 +88,11 @@ fun getPredicateGrpcRequest(
.build()
}

private fun getPredicateGrpcRequest(predicate: String, returnValue: String, type: Util.ValidationType): Util.PredicateInfo =
private fun getPredicateGrpcRequest(
predicate: String,
returnValue: String,
type: Util.ValidationType
): Util.PredicateInfo =
Util.PredicateInfo.newBuilder()
.setPredicate(predicate)
.setReturnValue(returnValue)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ class UTBotTargetsController(val project: Project) {
project,
getProjectTargetsGrpcRequest(project),
processTargets = { targetsResponse: Testgen.ProjectTargetsResponse ->
System.err.println("Received targets: $targetsResponse")
invokeOnEdt {
targetsToolWindow.setBusy(false)

Expand Down
3 changes: 3 additions & 0 deletions clion-plugin/src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -177,5 +177,8 @@
description="Generates tests for file without context of a project">
<synonym text="US"/>
</action>
<action id="org.utbot.cpp.clion.plugin.actions.generate.RunAllTestsWithCoverageAction"
class="org.utbot.cpp.clion.plugin.actions.generate.RunAllTestsWithCoverageAction"
text="UTBot: Run All Tests and Show Coverage"/>
</actions>
</idea-plugin>