Skip to content

Built-in SARIF reports visualizer #1166

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
Oct 19, 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
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ target/
.idea/
.gradle/
*.log
*.rdgen
*.rdgen
Original file line number Diff line number Diff line change
Expand Up @@ -176,13 +176,13 @@ private fun EngineProcessModel.setup(
synchronizer.measureExecutionForTermination(writeSarifReport) { params ->
val reportFilePath = Paths.get(params.reportFilePath)
reportFilePath.parent.toFile().mkdirs()
reportFilePath.toFile().writeText(
SarifReport(
testSets[params.testSetsId]!!,
params.generatedTestsCode,
RdSourceFindingStrategyFacade(realProtocol.rdSourceFindingStrategy)
).createReport().toJson()
)
val sarifReport = SarifReport(
testSets[params.testSetsId]!!,
params.generatedTestsCode,
RdSourceFindingStrategyFacade(realProtocol.rdSourceFindingStrategy)
).createReport().toJson()
reportFilePath.toFile().writeText(sarifReport)
sarifReport
}
synchronizer.measureExecutionForTermination(generateTestReport) { params ->
val eventLogMessage = params.eventLogMessage
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class EngineProcessModel private constructor(
private val _obtainClassId: RdCall<String, ByteArray>,
private val _findMethodsInClassMatchingSelected: RdCall<FindMethodsInClassMatchingSelectedArguments, FindMethodsInClassMatchingSelectedResult>,
private val _findMethodParamNames: RdCall<FindMethodParamNamesArguments, FindMethodParamNamesResult>,
private val _writeSarifReport: RdCall<WriteSarifReportArguments, Unit>,
private val _writeSarifReport: RdCall<WriteSarifReportArguments, String>,
private val _generateTestReport: RdCall<GenerateTestReportArgs, GenerateTestReportResult>
) : RdExtBase() {
//companion
Expand Down Expand Up @@ -73,7 +73,7 @@ class EngineProcessModel private constructor(
}


const val serializationHash = 3907671513584285891L
const val serializationHash = -621732450296355904L

}
override val serializersOwner: ISerializersOwner get() = EngineProcessModel
Expand All @@ -89,7 +89,7 @@ class EngineProcessModel private constructor(
val obtainClassId: RdCall<String, ByteArray> get() = _obtainClassId
val findMethodsInClassMatchingSelected: RdCall<FindMethodsInClassMatchingSelectedArguments, FindMethodsInClassMatchingSelectedResult> get() = _findMethodsInClassMatchingSelected
val findMethodParamNames: RdCall<FindMethodParamNamesArguments, FindMethodParamNamesResult> get() = _findMethodParamNames
val writeSarifReport: RdCall<WriteSarifReportArguments, Unit> get() = _writeSarifReport
val writeSarifReport: RdCall<WriteSarifReportArguments, String> get() = _writeSarifReport
val generateTestReport: RdCall<GenerateTestReportArgs, GenerateTestReportResult> get() = _generateTestReport
//methods
//initializer
Expand Down Expand Up @@ -133,7 +133,7 @@ class EngineProcessModel private constructor(
RdCall<String, ByteArray>(FrameworkMarshallers.String, FrameworkMarshallers.ByteArray),
RdCall<FindMethodsInClassMatchingSelectedArguments, FindMethodsInClassMatchingSelectedResult>(FindMethodsInClassMatchingSelectedArguments, FindMethodsInClassMatchingSelectedResult),
RdCall<FindMethodParamNamesArguments, FindMethodParamNamesResult>(FindMethodParamNamesArguments, FindMethodParamNamesResult),
RdCall<WriteSarifReportArguments, Unit>(WriteSarifReportArguments, FrameworkMarshallers.Void),
RdCall<WriteSarifReportArguments, String>(WriteSarifReportArguments, FrameworkMarshallers.String),
RdCall<GenerateTestReportArgs, GenerateTestReportResult>(GenerateTestReportArgs, GenerateTestReportResult)
)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package org.utbot.intellij.plugin.generator

import com.intellij.analysis.AnalysisScope
import com.intellij.codeInsight.CodeInsightUtil
import com.intellij.codeInsight.FileModificationService
import com.intellij.ide.fileTemplates.FileTemplateManager
import com.intellij.ide.fileTemplates.FileTemplateUtil
import com.intellij.ide.fileTemplates.JavaTemplateUtil
import com.intellij.ide.highlighter.JavaFileType
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.invokeLater
import com.intellij.openapi.application.runReadAction
import com.intellij.openapi.application.runWriteAction
import com.intellij.openapi.command.WriteCommandAction.runWriteCommandAction
Expand Down Expand Up @@ -51,12 +53,12 @@ import org.utbot.framework.codegen.model.UtilClassKind
import org.utbot.framework.codegen.model.UtilClassKind.Companion.UT_UTILS_CLASS_NAME
import org.utbot.framework.plugin.api.ClassId
import org.utbot.framework.plugin.api.CodegenLanguage
import org.utbot.intellij.plugin.inspection.UnitTestBotInspectionManager
import org.utbot.intellij.plugin.models.GenerateTestsModel
import org.utbot.intellij.plugin.models.packageName
import org.utbot.intellij.plugin.process.EngineProcess
import org.utbot.intellij.plugin.process.RdTestGenerationResult
import org.utbot.intellij.plugin.sarif.SarifReportIdea
import org.utbot.intellij.plugin.sarif.SourceFindingStrategyIdea
import org.utbot.intellij.plugin.ui.*
import org.utbot.intellij.plugin.ui.utils.getOrCreateSarifReportsPath
import org.utbot.intellij.plugin.ui.utils.showErrorDialogLater
Expand All @@ -65,6 +67,7 @@ import org.utbot.intellij.plugin.util.IntelliJApiHelper.Target.*
import org.utbot.intellij.plugin.util.IntelliJApiHelper.run
import org.utbot.intellij.plugin.util.RunConfigurationHelper
import org.utbot.intellij.plugin.util.extractClassMethodsIncludingNested
import org.utbot.sarif.Sarif
import org.utbot.sarif.SarifReport
import java.nio.file.Path
import java.util.concurrent.CancellationException
Expand Down Expand Up @@ -94,6 +97,7 @@ object CodeGenerationController {
val allTestPackages = getPackageDirectories(baseTestDirectory)
val latch = CountDownLatch(classesWithTests.size)
val testFilesPointers = mutableListOf<SmartPsiElementPointer<PsiFile>>()
val srcClassPathToSarifReport = mutableMapOf<Path, Sarif>()
val utilClassListener = UtilClassListener()
var index = 0
for ((srcClass, generateResult) in classesWithTests) {
Expand All @@ -119,6 +123,7 @@ object CodeGenerationController {
cut,
testClass,
testFilePointer,
srcClassPathToSarifReport,
model,
latch,
utilClassListener,
Expand Down Expand Up @@ -153,6 +158,10 @@ object CodeGenerationController {
}
proc.forceTermination()
UtTestsDialogProcessor.updateIndicator(indicator, UtTestsDialogProcessor.ProgressRange.SARIF, "Start tests with coverage", 1.0)

invokeLater {
runInspectionsIfNeeded(model.project, srcClassPathToSarifReport)
}
}
}
}
Expand All @@ -161,6 +170,25 @@ object CodeGenerationController {
}
}

/**
* Runs the UTBot inspection if there are detected errors.
*/
private fun runInspectionsIfNeeded(
project: Project,
srcClassPathToSarifReport: MutableMap<Path, Sarif>
) {
val sarifHasResults = srcClassPathToSarifReport.any { (_, sarif) ->
sarif.getAllResults().isNotEmpty()
}
if (!sarifHasResults) {
return
}
UnitTestBotInspectionManager
.getInstance(project, srcClassPathToSarifReport)
.createNewGlobalContext()
.doInspections(AnalysisScope(project))
}

private fun proceedTestReport(proc: EngineProcess, model: GenerateTestsModel) {
try {
// Parametrized tests are not supported in tests report yet
Expand Down Expand Up @@ -583,6 +611,7 @@ object CodeGenerationController {
classUnderTest: ClassId,
testClass: PsiClass,
filePointer: SmartPsiElementPointer<PsiFile>,
srcClassPathToSarifReport: MutableMap<Path, Sarif>,
model: GenerateTestsModel,
reportsCountDown: CountDownLatch,
utilClassListener: UtilClassListener,
Expand Down Expand Up @@ -661,16 +690,20 @@ object CodeGenerationController {
// uploading formatted code
val file = filePointer.containingFile

saveSarifReport(
val srcClassPath = srcClass.containingFile.virtualFile.toNioPath()
val sarifReport = saveSarifReport(
proc,
testSetsId,
testClassUpdated,
classUnderTest,
model,
reportsCountDown,
file?.text ?: generatedTestsCode,
srcClassPathToSarifReport,
srcClassPath,
indicator
)

unblockDocument(testClassUpdated.project, editor.document)
}
}
Expand Down Expand Up @@ -706,13 +739,26 @@ object CodeGenerationController {
model: GenerateTestsModel,
reportsCountDown: CountDownLatch,
generatedTestsCode: String,
srcClassPathToSarifReport: MutableMap<Path, Sarif>,
srcClassPath: Path,
indicator: ProgressIndicator
) {
val project = model.project

try {
// saving sarif report
SarifReportIdea.createAndSave(proc, testSetsId, testClassId, model, generatedTestsCode, testClass, reportsCountDown, indicator)
SarifReportIdea.createAndSave(
proc,
testSetsId,
testClassId,
model,
generatedTestsCode,
testClass,
reportsCountDown,
srcClassPathToSarifReport,
srcClassPath,
indicator
)
} catch (e: Exception) {
logger.error(e) { "error in saving sarif report"}
showErrorDialogLater(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package org.utbot.intellij.plugin.inspection

import com.intellij.codeInspection.LocalQuickFix
import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.icons.AllIcons
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.project.Project
import com.intellij.unscramble.AnalyzeStacktraceUtil

/**
* Button that launches the built-in "Analyze Stack Trace" action. Displayed as a quick fix.
*
* @param exceptionMessage short description of the detected exception.
* @param stackTraceLines list of strings of the form "className.methodName(fileName:lineNumber)".
*/
class AnalyzeStackTraceFix(
private val exceptionMessage: String,
private val stackTraceLines: List<String>
) : LocalQuickFix {

/**
* Without `invokeLater` the [com.intellij.execution.impl.ConsoleViewImpl.myPredefinedFilters] will not be filled.
*
* See [com.intellij.execution.impl.ConsoleViewImpl.createCompositeFilter] for more details.
*/
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
val stackTraceContent = stackTraceLines.joinToString("\n") { "at $it" }
ApplicationManager.getApplication().invokeLater {
AnalyzeStacktraceUtil.addConsole(
/* project = */ project,
/* consoleFactory = */ null,
/* tabTitle = */ "StackTrace",
/* text = */ "$exceptionMessage\n\n$stackTraceContent",
/* icon = */ AllIcons.Actions.Lightning
)
}
}

/**
* This text is displayed on the quick fix button.
*/
override fun getName() = "Analyze stack trace"

override fun getFamilyName() = name
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package org.utbot.intellij.plugin.inspection

import com.intellij.codeInspection.ex.*
import com.intellij.codeInspection.ui.InspectionToolPresentation
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.NotNullLazyValue
import com.intellij.ui.content.ContentManager
import org.utbot.sarif.Sarif
import java.nio.file.Path
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ConcurrentMap

/**
* Overrides some methods of [GlobalInspectionContextImpl] to satisfy the logic of [UnitTestBotInspectionTool].
*/
class UnitTestBotInspectionContext(
project: Project,
contentManager: NotNullLazyValue<out ContentManager>,
private val srcClassPathToSarifReport: MutableMap<Path, Sarif>
) : GlobalInspectionContextImpl(project, contentManager) {

/**
* See [GlobalInspectionContextImpl.myPresentationMap] for more details.
*/
private val myPresentationMap: ConcurrentMap<InspectionToolWrapper<*, *>, InspectionToolPresentation> =
ConcurrentHashMap()

private val globalInspectionToolWrapper by lazy {
val utbotInspectionTool = UnitTestBotInspectionTool.getInstance(srcClassPathToSarifReport)
GlobalInspectionToolWrapper(utbotInspectionTool).also {
it.initialize(/* context = */ this)
}
}

/**
* Returns [InspectionProfileImpl] with only one inspection tool - [UnitTestBotInspectionTool].
*/
override fun getCurrentProfile(): InspectionProfileImpl {
val supplier = InspectionToolsSupplier.Simple(listOf(globalInspectionToolWrapper))
return InspectionProfileImpl("UnitTestBot", supplier, BASE_PROFILE)
}

override fun close(noSuspiciousCodeFound: Boolean) {
myPresentationMap.clear()
super.close(noSuspiciousCodeFound)
}

override fun cleanup() {
myPresentationMap.clear()
super.cleanup()
}

/**
* Overriding is needed to provide [UnitTestBotInspectionToolPresentation]
* instead of the standard implementation of the [InspectionToolPresentation].
*/
override fun getPresentation(toolWrapper: InspectionToolWrapper<*, *>): InspectionToolPresentation {
return myPresentationMap.computeIfAbsent(toolWrapper) {
UnitTestBotInspectionToolPresentation(globalInspectionToolWrapper, context = this)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package org.utbot.intellij.plugin.inspection

import com.intellij.codeInspection.ex.GlobalInspectionContextImpl
import com.intellij.codeInspection.ex.InspectionManagerEx
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.NotNullLazyValue
import com.intellij.ui.content.ContentManager
import org.utbot.sarif.Sarif
import java.nio.file.Path

/**
* Overrides some methods of [InspectionManagerEx] to satisfy the logic of [UnitTestBotInspectionTool].
*/
class UnitTestBotInspectionManager(project: Project) : InspectionManagerEx(project) {

private var srcClassPathToSarifReport: MutableMap<Path, Sarif> = mutableMapOf()

companion object {
fun getInstance(project: Project, srcClassPathToSarifReport: MutableMap<Path, Sarif>) =
UnitTestBotInspectionManager(project).also {
it.srcClassPathToSarifReport = srcClassPathToSarifReport
}
}

/**
* See [InspectionManagerEx.myContentManager] for more details.
*/
private val myContentManager: NotNullLazyValue<ContentManager> by lazy {
NotNullLazyValue.createValue {
getProblemsViewContentManager(project)
}
}

/**
* Overriding is needed to provide [UnitTestBotInspectionContext] instead of [GlobalInspectionContextImpl].
*/
override fun createNewGlobalContext(): GlobalInspectionContextImpl =
UnitTestBotInspectionContext(project, myContentManager, srcClassPathToSarifReport)
}
Loading