From 8aecd9991bcf468d1c932433d68f6a15ee9e9883 Mon Sep 17 00:00:00 2001 From: Nikita Stroganov Date: Mon, 19 Sep 2022 19:29:33 +0300 Subject: [PATCH 01/10] The first version of the utbot inspection tool (#972) --- .../org/utbot/cli/GenerateTestsCommand.kt | 2 +- .../kotlin/org/utbot/sarif/SarifReportTest.kt | 28 +++--- .../GenerateTestsAndSarifReportFacade.kt | 2 +- .../kotlin/org/utbot/sarif/DataClasses.kt | 19 +++- .../kotlin/org/utbot/sarif/SarifReport.kt | 45 +++------- .../inspection/UTBotInspectionContext.kt | 28 ++++++ .../inspection/UTBotInspectionManager.kt | 37 ++++++++ .../plugin/inspection/UTBotInspectionTool.kt | 89 +++++++++++++++++++ .../plugin/inspection/ViewGeneratedTestFix.kt | 41 +++++++++ .../src/main/resources/META-INF/plugin.xml | 11 +++ .../UTBotInspectionTool.html | 11 +++ 11 files changed, 261 insertions(+), 52 deletions(-) create mode 100644 utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/UTBotInspectionContext.kt create mode 100644 utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/UTBotInspectionManager.kt create mode 100644 utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/UTBotInspectionTool.kt create mode 100644 utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/ViewGeneratedTestFix.kt create mode 100644 utbot-intellij/src/main/resources/inspectionDescriptions/UTBotInspectionTool.html diff --git a/utbot-cli/src/main/kotlin/org/utbot/cli/GenerateTestsCommand.kt b/utbot-cli/src/main/kotlin/org/utbot/cli/GenerateTestsCommand.kt index 35d69dcd8a..b0b7101011 100644 --- a/utbot-cli/src/main/kotlin/org/utbot/cli/GenerateTestsCommand.kt +++ b/utbot-cli/src/main/kotlin/org/utbot/cli/GenerateTestsCommand.kt @@ -149,7 +149,7 @@ class GenerateTestsCommand : else -> { val sourceFinding = SourceFindingStrategyDefault(classFqn, sourceCodeFile, testsFilePath, projectRootPath) - val report = SarifReport(testSets, testClassBody, sourceFinding).createReport() + val report = SarifReport(testSets, testClassBody, sourceFinding).createReport().toJson() saveToFile(report, sarifReport) println("The report was saved to \"$sarifReport\".") } diff --git a/utbot-framework-test/src/test/kotlin/org/utbot/sarif/SarifReportTest.kt b/utbot-framework-test/src/test/kotlin/org/utbot/sarif/SarifReportTest.kt index 888d02a816..44aa51e5f3 100644 --- a/utbot-framework-test/src/test/kotlin/org/utbot/sarif/SarifReportTest.kt +++ b/utbot-framework-test/src/test/kotlin/org/utbot/sarif/SarifReportTest.kt @@ -1,7 +1,5 @@ package org.utbot.sarif -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import com.fasterxml.jackson.module.kotlin.readValue import org.junit.Test import org.mockito.Mockito import org.utbot.framework.plugin.api.ExecutableId @@ -19,7 +17,7 @@ class SarifReportTest { testSets = listOf(), generatedTestsCode = "", sourceFindingEmpty - ).createReport() + ).createReport().toJson() assert(actualReport.isNotEmpty()) } @@ -30,7 +28,7 @@ class SarifReportTest { testSets = listOf(testSet), generatedTestsCode = "", sourceFindingEmpty - ).createReport().toSarif() + ).createReport() assert(sarif.runs.first().results.isEmpty()) } @@ -60,7 +58,7 @@ class SarifReportTest { testSets = testSets, generatedTestsCode = "", sourceFindingEmpty - ).createReport().toSarif() + ).createReport() assert(report.runs.first().results[0].message.text.contains("NullPointerException")) assert(report.runs.first().results[1].message.text.contains("ArrayIndexOutOfBoundsException")) @@ -77,7 +75,7 @@ class SarifReportTest { Mockito.`when`(mockUtExecution.path.lastOrNull()?.stmt?.javaSourceStartLineNumber).thenReturn(1337) Mockito.`when`(mockUtExecution.testMethodName).thenReturn("testMain_ThrowArithmeticException") - val report = sarifReportMain.createReport().toSarif() + val report = sarifReportMain.createReport() val result = report.runs.first().results.first() val location = result.locations.first().physicalLocation @@ -105,7 +103,7 @@ class SarifReportTest { ) ) - val report = sarifReportMain.createReport().toSarif() + val report = sarifReportMain.createReport() val result = report.runs.first().results.first() assert(result.message.text.contains("227")) @@ -128,7 +126,7 @@ class SarifReportTest { ) Mockito.`when`(mockUtExecution.stateBefore.parameters).thenReturn(listOf()) - val report = sarifReportMain.createReport().toSarif() + val report = sarifReportMain.createReport() val result = report.runs.first().results.first().codeFlows.first().threadFlows.first().locations.map { it.location.physicalLocation @@ -153,7 +151,7 @@ class SarifReportTest { Mockito.`when`(mockUtExecution.stateBefore.parameters).thenReturn(listOf()) Mockito.`when`(mockUtExecution.testMethodName).thenReturn("testMain_ThrowArithmeticException") - val report = sarifReportMain.createReport().toSarif() + val report = sarifReportMain.createReport() val codeFlowPhysicalLocations = report.runs[0].results[0].codeFlows[0].threadFlows[0].locations.map { it.location.physicalLocation @@ -177,7 +175,7 @@ class SarifReportTest { Mockito.`when`(mockUtExecution.stateBefore.parameters).thenReturn(listOf()) Mockito.`when`(mockUtExecution.testMethodName).thenReturn("testMain_ThrowArithmeticException") - val report = sarifReportPrivateMain.createReport().toSarif() + val report = sarifReportPrivateMain.createReport() val codeFlowPhysicalLocations = report.runs[0].results[0].codeFlows[0].threadFlows[0].locations.map { it.location.physicalLocation @@ -203,7 +201,7 @@ class SarifReportTest { testSets = testSets, generatedTestsCode = "", sourceFindingMain - ).createReport().toSarif() + ).createReport() assert(report.runs.first().results.size == 1) // no duplicates } @@ -228,7 +226,7 @@ class SarifReportTest { testSets = testSets, generatedTestsCode = "", sourceFindingMain - ).createReport().toSarif() + ).createReport() assert(report.runs.first().results.size == 2) // no results have been removed } @@ -257,7 +255,7 @@ class SarifReportTest { testSets = testSets, generatedTestsCode = "", sourceFindingMain - ).createReport().toSarif() + ).createReport() assert(report.runs.first().results.size == 2) // no results have been removed } @@ -291,7 +289,7 @@ class SarifReportTest { testSets = testSets, generatedTestsCode = "", sourceFindingMain - ).createReport().toSarif() + ).createReport() assert(report.runs.first().results.size == 1) // no duplicates assert(report.runs.first().results.first().totalCodeFlowLocations() == 1) // with a shorter stack trace @@ -310,8 +308,6 @@ class SarifReportTest { Mockito.`when`(mockExecutableId.classId.name).thenReturn("Main") } - private fun String.toSarif(): Sarif = jacksonObjectMapper().readValue(this) - // constants private val sourceFindingEmpty = SourceFindingStrategyDefault( diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/sarif/GenerateTestsAndSarifReportFacade.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/sarif/GenerateTestsAndSarifReportFacade.kt index 4c41010460..b0a2800086 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/sarif/GenerateTestsAndSarifReportFacade.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/sarif/GenerateTestsAndSarifReportFacade.kt @@ -91,7 +91,7 @@ class GenerateTestsAndSarifReportFacade( testClassBody: String, sourceFinding: SourceFindingStrategy ) { - val sarifReport = SarifReport(testSets, testClassBody, sourceFinding).createReport() + val sarifReport = SarifReport(testSets, testClassBody, sourceFinding).createReport().toJson() targetClass.sarifReportFile.writeText(sarifReport) } } \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/sarif/DataClasses.kt b/utbot-framework/src/main/kotlin/org/utbot/sarif/DataClasses.kt index 5ecae62040..1cc571f047 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/sarif/DataClasses.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/sarif/DataClasses.kt @@ -1,7 +1,10 @@ package org.utbot.sarif +import com.fasterxml.jackson.annotation.JsonInclude import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.annotation.JsonValue +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import com.fasterxml.jackson.module.kotlin.readValue /** * Useful links: @@ -24,7 +27,19 @@ data class Sarif( fun fromRun(run: SarifRun) = Sarif(defaultSchema, defaultVersion, listOf(run)) + + fun fromJson(reportInJson: String): Sarif = + jacksonObjectMapper().readValue(reportInJson) } + + fun toJson(): String = + jacksonObjectMapper() + .setSerializationInclusion(JsonInclude.Include.NON_NULL) + .writerWithDefaultPrettyPrinter() + .writeValueAsString(this) + + fun getAllResults(): List = + runs.flatMap { it.results } } /** @@ -104,8 +119,8 @@ data class SarifResult( * Returns the total number of locations in all [codeFlows]. */ fun totalCodeFlowLocations() = - codeFlows.sumBy { codeFlow -> - codeFlow.threadFlows.sumBy { threadFlow -> + codeFlows.sumOf { codeFlow -> + codeFlow.threadFlows.sumOf { threadFlow -> threadFlow.locations.size } } diff --git a/utbot-framework/src/main/kotlin/org/utbot/sarif/SarifReport.kt b/utbot-framework/src/main/kotlin/org/utbot/sarif/SarifReport.kt index 2166156bac..da762d4b3e 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/sarif/SarifReport.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/sarif/SarifReport.kt @@ -1,8 +1,5 @@ package org.utbot.sarif -import com.fasterxml.jackson.annotation.JsonInclude -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import com.fasterxml.jackson.module.kotlin.readValue import org.utbot.common.PathUtil.fileExtension import org.utbot.common.PathUtil.toPath import org.utbot.framework.UtSettings @@ -21,45 +18,20 @@ class SarifReport( private val generatedTestsCode: String, private val sourceFinding: SourceFindingStrategy ) { - companion object { - /** * Merges several SARIF reports given as JSON-strings into one */ fun mergeReports(reports: List): String = reports.fold(Sarif.empty()) { sarif: Sarif, report: String -> - sarif.copy(runs = sarif.runs + report.jsonToSarif().runs) - }.sarifToJson() - - // internal - - private fun String.jsonToSarif(): Sarif = - jacksonObjectMapper().readValue(this) - - private fun Sarif.sarifToJson(): String = - jacksonObjectMapper() - .setSerializationInclusion(JsonInclude.Include.NON_NULL) - .writerWithDefaultPrettyPrinter() - .writeValueAsString(this) + sarif.copy(runs = sarif.runs + Sarif.fromJson(report).runs) + }.toJson() } /** - * Creates a SARIF report and returns it as string - */ - fun createReport(): String = - constructSarif().sarifToJson() - - // internal - - private val defaultLineNumber = 1 // if the line in the source code where the exception is thrown is unknown - - /** - * [Read more about links to locations](https://github.com/microsoft/sarif-tutorials/blob/main/docs/3-Beyond-basics.md#msg-links-location) + * Creates a SARIF report. */ - private val relatedLocationId = 1 // for attaching link to generated test in related locations - - private fun constructSarif(): Sarif { + fun createReport(): Sarif { val sarifResults = mutableListOf() val sarifRules = mutableSetOf() @@ -85,6 +57,15 @@ class SarifReport( ) } + // internal + + private val defaultLineNumber = 1 // if the line in the source code where the exception is thrown is unknown + + /** + * [Read more about links to locations](https://github.com/microsoft/sarif-tutorials/blob/main/docs/3-Beyond-basics.md#msg-links-location) + */ + private val relatedLocationId = 1 // for attaching link to generated test in related locations + /** * Minimizes detected errors and removes duplicates. * diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/UTBotInspectionContext.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/UTBotInspectionContext.kt new file mode 100644 index 0000000000..b8b8e22d27 --- /dev/null +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/UTBotInspectionContext.kt @@ -0,0 +1,28 @@ +package org.utbot.intellij.plugin.inspection + +import com.intellij.codeInspection.ex.* +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 + +/** + * Provides [InspectionProfileImpl] with only one inspection tool - [UTBotInspectionTool]. + * + * @see GlobalInspectionContextImpl + */ +class UTBotInspectionContext( + project: Project, + contentManager: NotNullLazyValue, + val sarifReports: MutableMap +) : GlobalInspectionContextImpl(project, contentManager) { + + override fun getCurrentProfile(): InspectionProfileImpl { + val utbotInspectionTool = UTBotInspectionTool.getInstance(sarifReports) + val globalInspectionToolWrapper = GlobalInspectionToolWrapper(utbotInspectionTool) + globalInspectionToolWrapper.initialize(this) + val supplier = InspectionToolsSupplier.Simple(listOf(globalInspectionToolWrapper)) + return InspectionProfileImpl("UTBotInspectionToolProfile", supplier, BASE_PROFILE) + } +} diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/UTBotInspectionManager.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/UTBotInspectionManager.kt new file mode 100644 index 0000000000..33a3618d62 --- /dev/null +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/UTBotInspectionManager.kt @@ -0,0 +1,37 @@ +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 + +/** + * Creates [UTBotInspectionContext] with right arguments. + * + * Inheritance is needed to provide [UTBotInspectionContext] instead of [GlobalInspectionContextImpl]. + * + * See [com.intellij.codeInspection.ex.InspectionManagerEx] for details. + */ +class UTBotInspectionManager(project: Project) : InspectionManagerEx(project) { + + companion object { + fun getInstance(project: Project, sarifReports: MutableMap) = + UTBotInspectionManager(project).also { + it.sarifReports = sarifReports + } + } + + private val myContentManager: NotNullLazyValue by lazy { + NotNullLazyValue.createValue { + getProblemsViewContentManager(project) + } + } + + private var sarifReports: MutableMap = mutableMapOf() + + override fun createNewGlobalContext(): GlobalInspectionContextImpl = + UTBotInspectionContext(project, myContentManager, sarifReports) +} diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/UTBotInspectionTool.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/UTBotInspectionTool.kt new file mode 100644 index 0000000000..1c5a46cb36 --- /dev/null +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/UTBotInspectionTool.kt @@ -0,0 +1,89 @@ +package org.utbot.intellij.plugin.inspection + +import com.intellij.codeInspection.* +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.TextRange +import com.intellij.psi.PsiDocumentManager +import com.intellij.psi.PsiFile +import org.utbot.sarif.Sarif +import org.utbot.sarif.SarifRegion +import java.nio.file.Path + +/** + * Global inspection tool that displays detected errors from the SARIF report. + */ +class UTBotInspectionTool : GlobalSimpleInspectionTool() { + + private var sarifReports: MutableMap = mutableMapOf() + + companion object { + fun getInstance(sarifReports: MutableMap) = + UTBotInspectionTool().also { + it.sarifReports = sarifReports + } + } + + override fun getShortName() = "UTBotInspectionTool" + + override fun getDisplayName() = "Unchecked exceptions" + + override fun getGroupDisplayName() = "Errors detected by UTBot" + + override fun checkFile( + psiFile: PsiFile, + manager: InspectionManager, + problemsHolder: ProblemsHolder, + globalContext: GlobalInspectionContext, + problemDescriptionsProcessor: ProblemDescriptionsProcessor + ) { + val sarifReport = sarifReports[psiFile.virtualFile.toNioPath()] + ?: return // no results for this file + + for (sarifResult in sarifReport.getAllResults()) { + val srcFileLocation = sarifResult.locations.firstOrNull() ?: continue + val errorRegion = srcFileLocation.physicalLocation.region + val errorTextRange = getTextRange(problemsHolder.project, psiFile, errorRegion) + + // see `org.utbot.sarif.SarifReport.processUncheckedException` for the message template + val errorMessage = sarifResult.message.text.split('\n').take(2).joinToString(" ") + + val testFileLocation = sarifResult.relatedLocations.firstOrNull()?.physicalLocation + val viewGeneratedTestFix = testFileLocation?.let { + ViewGeneratedTestFix( + testFileRelativePath = it.artifactLocation.uri, + lineNumber = it.region.startLine, + columnNumber = it.region.startColumn ?: 1 + ) + } + + val problemDescriptor = problemsHolder.manager.createProblemDescriptor( + psiFile, + errorTextRange, + errorMessage, + ProblemHighlightType.ERROR, + /* onTheFly = */ true, + viewGeneratedTestFix + ) + problemDescriptionsProcessor.addProblemElement( + globalContext.refManager.getReference(psiFile), + problemDescriptor + ) + } + } + + // internal + + private fun getTextRange(project: Project, file: PsiFile, region: SarifRegion): TextRange { + val documentManager = PsiDocumentManager.getInstance(project) + val document = documentManager.getDocument(file.containingFile) + ?: return TextRange.EMPTY_RANGE + + val lineNumber = region.startLine - 1 // to 0-based + val columnNumber = region.startColumn ?: 1 + + val lineStartOffset = document.getLineStartOffset(lineNumber) + columnNumber - 1 + val lineEndOffset = document.getLineEndOffset(lineNumber) + return TextRange(lineStartOffset, lineEndOffset) + } +} + diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/ViewGeneratedTestFix.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/ViewGeneratedTestFix.kt new file mode 100644 index 0000000000..c0c9509fce --- /dev/null +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/ViewGeneratedTestFix.kt @@ -0,0 +1,41 @@ +package org.utbot.intellij.plugin.inspection + +import com.intellij.codeInspection.LocalQuickFix +import com.intellij.codeInspection.ProblemDescriptor +import com.intellij.openapi.editor.LogicalPosition +import com.intellij.openapi.fileEditor.FileEditorManager +import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.VfsUtil +import org.utbot.common.PathUtil.toPath + +/** + * Button with a link to the [testFileRelativePath]. + * Displayed as "quick fix". + * + * @param testFileRelativePath path to the generated test file. + * Should be relative to the project root + * @param lineNumber one-based line number + * @param columnNumber one-based column number + */ +class ViewGeneratedTestFix( + val testFileRelativePath: String, + val lineNumber: Int, + val columnNumber: Int +) : LocalQuickFix { + + override fun applyFix(project: Project, descriptor: ProblemDescriptor) { + val testFileAbsolutePath = project.basePath?.toPath()?.resolve(testFileRelativePath) ?: return + val virtualFile = VfsUtil.findFile(testFileAbsolutePath, /* refreshIfNeeded = */ true) ?: return + val editor = FileEditorManager.getInstance(project) + editor.openFile(virtualFile, /* focusEditor = */ true) + val caretModel = editor.selectedTextEditor?.caretModel ?: return + val zeroBasedPosition = LogicalPosition(lineNumber - 1, columnNumber - 1) + caretModel.moveToLogicalPosition(zeroBasedPosition) + val selectionModel = editor.selectedTextEditor?.selectionModel ?: return + selectionModel.selectLineAtCaret() + } + + override fun getName() = "View generated test" + + override fun getFamilyName() = name +} \ No newline at end of file diff --git a/utbot-intellij/src/main/resources/META-INF/plugin.xml b/utbot-intellij/src/main/resources/META-INF/plugin.xml index 81f3409165..bf9bb030a2 100644 --- a/utbot-intellij/src/main/resources/META-INF/plugin.xml +++ b/utbot-intellij/src/main/resources/META-INF/plugin.xml @@ -88,4 +88,15 @@ ]]> + + + + diff --git a/utbot-intellij/src/main/resources/inspectionDescriptions/UTBotInspectionTool.html b/utbot-intellij/src/main/resources/inspectionDescriptions/UTBotInspectionTool.html new file mode 100644 index 0000000000..3b37d2aeaf --- /dev/null +++ b/utbot-intellij/src/main/resources/inspectionDescriptions/UTBotInspectionTool.html @@ -0,0 +1,11 @@ + + +

Reports unchecked exceptions detected by UTBot.

+

Example:

+
+void foo(int a) {
+    return 1 / a; // throws ArithmeticException when `a == 0`
+}
+
+ + \ No newline at end of file From 9562cb3983616459ee054a2e8b52f59db7633b65 Mon Sep 17 00:00:00 2001 From: Nikita Stroganov Date: Thu, 22 Sep 2022 14:10:51 +0300 Subject: [PATCH 02/10] Disable suppressing quick fixes after applying --- .../inspection/UTBotInspectionContext.kt | 48 ++++++++++++++++--- .../inspection/UTBotInspectionManager.kt | 22 +++++---- .../plugin/inspection/UTBotInspectionTool.kt | 17 +++++-- .../UTBotInspectionToolPresentation.kt | 24 ++++++++++ .../plugin/inspection/ViewGeneratedTestFix.kt | 3 +- 5 files changed, 91 insertions(+), 23 deletions(-) create mode 100644 utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/UTBotInspectionToolPresentation.kt diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/UTBotInspectionContext.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/UTBotInspectionContext.kt index b8b8e22d27..674b69271d 100644 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/UTBotInspectionContext.kt +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/UTBotInspectionContext.kt @@ -1,28 +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 /** - * Provides [InspectionProfileImpl] with only one inspection tool - [UTBotInspectionTool]. - * - * @see GlobalInspectionContextImpl + * Overrides some methods of [GlobalInspectionContextImpl] to satisfy the logic of [UTBotInspectionTool]. */ class UTBotInspectionContext( project: Project, contentManager: NotNullLazyValue, - val sarifReports: MutableMap + val srcClassPathToSarifReport: MutableMap ) : GlobalInspectionContextImpl(project, contentManager) { + /** + * See [GlobalInspectionContextImpl.myPresentationMap] for more details. + */ + private val myPresentationMap: ConcurrentMap, InspectionToolPresentation> = + ConcurrentHashMap() + + private val globalInspectionToolWrapper by lazy { + val utbotInspectionTool = UTBotInspectionTool.getInstance(srcClassPathToSarifReport) + GlobalInspectionToolWrapper(utbotInspectionTool).also { + it.initialize(/* context = */ this) + } + } + + /** + * Returns [InspectionProfileImpl] with only one inspection tool - [UTBotInspectionTool]. + */ override fun getCurrentProfile(): InspectionProfileImpl { - val utbotInspectionTool = UTBotInspectionTool.getInstance(sarifReports) - val globalInspectionToolWrapper = GlobalInspectionToolWrapper(utbotInspectionTool) - globalInspectionToolWrapper.initialize(this) val supplier = InspectionToolsSupplier.Simple(listOf(globalInspectionToolWrapper)) return InspectionProfileImpl("UTBotInspectionToolProfile", 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 [UTBotInspectionToolPresentation] + * instead of the standard implementation of the [InspectionToolPresentation]. + */ + override fun getPresentation(toolWrapper: InspectionToolWrapper<*, *>): InspectionToolPresentation { + return myPresentationMap.computeIfAbsent(toolWrapper) { + UTBotInspectionToolPresentation(globalInspectionToolWrapper, context = this) + } + } } diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/UTBotInspectionManager.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/UTBotInspectionManager.kt index 33a3618d62..813e6d3b65 100644 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/UTBotInspectionManager.kt +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/UTBotInspectionManager.kt @@ -9,29 +9,31 @@ import org.utbot.sarif.Sarif import java.nio.file.Path /** - * Creates [UTBotInspectionContext] with right arguments. - * - * Inheritance is needed to provide [UTBotInspectionContext] instead of [GlobalInspectionContextImpl]. - * - * See [com.intellij.codeInspection.ex.InspectionManagerEx] for details. + * Overrides some methods of [InspectionManagerEx] to satisfy the logic of [UTBotInspectionTool]. */ class UTBotInspectionManager(project: Project) : InspectionManagerEx(project) { + private var srcClassPathToSarifReport: MutableMap = mutableMapOf() + companion object { - fun getInstance(project: Project, sarifReports: MutableMap) = + fun getInstance(project: Project, srcClassPathToSarifReport: MutableMap) = UTBotInspectionManager(project).also { - it.sarifReports = sarifReports + it.srcClassPathToSarifReport = srcClassPathToSarifReport } } + /** + * See [InspectionManagerEx.myContentManager] for more details. + */ private val myContentManager: NotNullLazyValue by lazy { NotNullLazyValue.createValue { getProblemsViewContentManager(project) } } - private var sarifReports: MutableMap = mutableMapOf() - + /** + * Overriding is needed to provide [UTBotInspectionContext] instead of [GlobalInspectionContextImpl]. + */ override fun createNewGlobalContext(): GlobalInspectionContextImpl = - UTBotInspectionContext(project, myContentManager, sarifReports) + UTBotInspectionContext(project, myContentManager, srcClassPathToSarifReport) } diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/UTBotInspectionTool.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/UTBotInspectionTool.kt index 1c5a46cb36..e4bf4edb84 100644 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/UTBotInspectionTool.kt +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/UTBotInspectionTool.kt @@ -14,12 +14,15 @@ import java.nio.file.Path */ class UTBotInspectionTool : GlobalSimpleInspectionTool() { - private var sarifReports: MutableMap = mutableMapOf() + /** + * Map from the path to the class under test to [Sarif] for it. + */ + private var srcClassPathToSarifReport: MutableMap = mutableMapOf() companion object { - fun getInstance(sarifReports: MutableMap) = + fun getInstance(srcClassPathToSarifReport: MutableMap) = UTBotInspectionTool().also { - it.sarifReports = sarifReports + it.srcClassPathToSarifReport = srcClassPathToSarifReport } } @@ -29,6 +32,9 @@ class UTBotInspectionTool : GlobalSimpleInspectionTool() { override fun getGroupDisplayName() = "Errors detected by UTBot" + /** + * Appends all the errors from the SARIF report for [psiFile] to the [problemDescriptionsProcessor]. + */ override fun checkFile( psiFile: PsiFile, manager: InspectionManager, @@ -36,7 +42,7 @@ class UTBotInspectionTool : GlobalSimpleInspectionTool() { globalContext: GlobalInspectionContext, problemDescriptionsProcessor: ProblemDescriptionsProcessor ) { - val sarifReport = sarifReports[psiFile.virtualFile.toNioPath()] + val sarifReport = srcClassPathToSarifReport[psiFile.virtualFile.toNioPath()] ?: return // no results for this file for (sarifResult in sarifReport.getAllResults()) { @@ -73,6 +79,9 @@ class UTBotInspectionTool : GlobalSimpleInspectionTool() { // internal + /** + * Converts [SarifRegion] to the [TextRange] of the given [file]. + */ private fun getTextRange(project: Project, file: PsiFile, region: SarifRegion): TextRange { val documentManager = PsiDocumentManager.getInstance(project) val document = documentManager.getDocument(file.containingFile) diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/UTBotInspectionToolPresentation.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/UTBotInspectionToolPresentation.kt new file mode 100644 index 0000000000..cb6bad12a9 --- /dev/null +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/UTBotInspectionToolPresentation.kt @@ -0,0 +1,24 @@ +package org.utbot.intellij.plugin.inspection + +import com.intellij.codeInspection.CommonProblemDescriptor +import com.intellij.codeInspection.ex.InspectionToolWrapper +import com.intellij.codeInspection.ui.DefaultInspectionToolPresentation + +/** + * Overrides [resolveProblem] to avoid suppressing the button [ViewGeneratedTestFix]. + */ +class UTBotInspectionToolPresentation( + toolWrapper: InspectionToolWrapper<*, *>, + context: UTBotInspectionContext +) : DefaultInspectionToolPresentation(toolWrapper, context) { + + /** + * This method is called when the user clicks on the quick fix button. + * In the case of [UTBotInspectionTool] we do not want to remove the button after applying the fix. + * + * See [DefaultInspectionToolPresentation.resolveProblem] for more details. + */ + override fun resolveProblem(descriptor: CommonProblemDescriptor) { + // nothing + } +} \ No newline at end of file diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/ViewGeneratedTestFix.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/ViewGeneratedTestFix.kt index c0c9509fce..d017c30de9 100644 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/ViewGeneratedTestFix.kt +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/ViewGeneratedTestFix.kt @@ -9,8 +9,7 @@ import com.intellij.openapi.vfs.VfsUtil import org.utbot.common.PathUtil.toPath /** - * Button with a link to the [testFileRelativePath]. - * Displayed as "quick fix". + * Button with a link to the [testFileRelativePath]. Displayed as a quick fix. * * @param testFileRelativePath path to the generated test file. * Should be relative to the project root From 6869555d5b1a04da2ea66985cd3331df8037c122 Mon Sep 17 00:00:00 2001 From: Nikita Stroganov Date: Mon, 26 Sep 2022 21:45:40 +0300 Subject: [PATCH 03/10] Add analyze stack trace button --- .../kotlin/org/utbot/sarif/SarifReport.kt | 7 +++- .../plugin/inspection/AnalyzeStackTraceFix.kt | 36 +++++++++++++++++++ .../plugin/inspection/UTBotInspectionTool.kt | 27 +++++++++++--- 3 files changed, 64 insertions(+), 6 deletions(-) create mode 100644 utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/AnalyzeStackTraceFix.kt diff --git a/utbot-framework/src/main/kotlin/org/utbot/sarif/SarifReport.kt b/utbot-framework/src/main/kotlin/org/utbot/sarif/SarifReport.kt index da762d4b3e..670e76e4f9 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/sarif/SarifReport.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/sarif/SarifReport.kt @@ -4,6 +4,7 @@ import org.utbot.common.PathUtil.fileExtension import org.utbot.common.PathUtil.toPath import org.utbot.framework.UtSettings import org.utbot.framework.plugin.api.* +import kotlin.io.path.nameWithoutExtension /** * Used for the SARIF report creation by given test cases and generated tests code. @@ -208,10 +209,14 @@ class SarifReport( val methodCallLocation: SarifPhysicalLocation? = findMethodCallInTestBody(utExecution.testMethodName, method.name) if (methodCallLocation != null) { + val testFileName = sourceFinding.testsRelativePath.toPath().fileName + val testClassName = testFileName.nameWithoutExtension + val testMethodName = utExecution.testMethodName + val methodCallLineNumber = methodCallLocation.region.startLine val methodCallLocationWrapper = SarifFlowLocationWrapper( SarifFlowLocation( message = Message( - text = "${sourceFinding.testsRelativePath.toPath().fileName}:${methodCallLocation.region.startLine}" + text = "$testClassName.$testMethodName($testFileName:$methodCallLineNumber)" ), physicalLocation = methodCallLocation ) diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/AnalyzeStackTraceFix.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/AnalyzeStackTraceFix.kt new file mode 100644 index 0000000000..a82e5cbf94 --- /dev/null +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/AnalyzeStackTraceFix.kt @@ -0,0 +1,36 @@ +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 + +class AnalyzeStackTraceFix( + private val exceptionMessage: String, + private val stackTraceLines: List +) : 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 + ) + } + } + + override fun getName() = "Analyze stack trace" + + override fun getFamilyName() = name +} \ No newline at end of file diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/UTBotInspectionTool.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/UTBotInspectionTool.kt index e4bf4edb84..c3e20a8500 100644 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/UTBotInspectionTool.kt +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/UTBotInspectionTool.kt @@ -7,6 +7,7 @@ import com.intellij.psi.PsiDocumentManager import com.intellij.psi.PsiFile import org.utbot.sarif.Sarif import org.utbot.sarif.SarifRegion +import org.utbot.sarif.SarifResult import java.nio.file.Path /** @@ -46,12 +47,16 @@ class UTBotInspectionTool : GlobalSimpleInspectionTool() { ?: return // no results for this file for (sarifResult in sarifReport.getAllResults()) { - val srcFileLocation = sarifResult.locations.firstOrNull() ?: continue + val srcFileLocation = sarifResult.locations.firstOrNull() + ?: continue + val errorRegion = srcFileLocation.physicalLocation.region val errorTextRange = getTextRange(problemsHolder.project, psiFile, errorRegion) // see `org.utbot.sarif.SarifReport.processUncheckedException` for the message template - val errorMessage = sarifResult.message.text.split('\n').take(2).joinToString(" ") + val (exceptionMessage, testCaseMessage) = + sarifResult.message.text.split('\n').take(2) + val sarifResultMessage = "$exceptionMessage $testCaseMessage" val testFileLocation = sarifResult.relatedLocations.firstOrNull()?.physicalLocation val viewGeneratedTestFix = testFileLocation?.let { @@ -62,13 +67,17 @@ class UTBotInspectionTool : GlobalSimpleInspectionTool() { ) } + val stackTraceLines = sarifResult.extractStackTraceLines() + val analyzeStackTraceFix = AnalyzeStackTraceFix(exceptionMessage, stackTraceLines) + val problemDescriptor = problemsHolder.manager.createProblemDescriptor( psiFile, errorTextRange, - errorMessage, + sarifResultMessage, ProblemHighlightType.ERROR, /* onTheFly = */ true, - viewGeneratedTestFix + viewGeneratedTestFix, + analyzeStackTraceFix ) problemDescriptionsProcessor.addProblemElement( globalContext.refManager.getReference(psiFile), @@ -94,5 +103,13 @@ class UTBotInspectionTool : GlobalSimpleInspectionTool() { val lineEndOffset = document.getLineEndOffset(lineNumber) return TextRange(lineStartOffset, lineEndOffset) } -} + private fun SarifResult.extractStackTraceLines(): List = + this.codeFlows.flatMap { sarifCodeFlow -> + sarifCodeFlow.threadFlows.flatMap { sarifThreadFlow -> + sarifThreadFlow.locations.map { sarifFlowLocationWrapper -> + sarifFlowLocationWrapper.location.message.text + } + } + }.reversed() +} From 1804b802e140c7209fd4c7985ab4cc67426dcdee Mon Sep 17 00:00:00 2001 From: Nikita Stroganov Date: Wed, 28 Sep 2022 10:30:48 +0300 Subject: [PATCH 04/10] Add comments --- .../intellij/plugin/inspection/AnalyzeStackTraceFix.kt | 9 +++++++++ .../plugin/inspection/UTBotInspectionToolPresentation.kt | 2 +- .../intellij/plugin/inspection/ViewGeneratedTestFix.kt | 6 ++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/AnalyzeStackTraceFix.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/AnalyzeStackTraceFix.kt index a82e5cbf94..0ebb9a5c8e 100644 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/AnalyzeStackTraceFix.kt +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/AnalyzeStackTraceFix.kt @@ -7,6 +7,12 @@ 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 @@ -30,6 +36,9 @@ class AnalyzeStackTraceFix( } } + /** + * This text is displayed on the quick fix button. + */ override fun getName() = "Analyze stack trace" override fun getFamilyName() = name diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/UTBotInspectionToolPresentation.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/UTBotInspectionToolPresentation.kt index cb6bad12a9..7d1d5a69a7 100644 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/UTBotInspectionToolPresentation.kt +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/UTBotInspectionToolPresentation.kt @@ -5,7 +5,7 @@ import com.intellij.codeInspection.ex.InspectionToolWrapper import com.intellij.codeInspection.ui.DefaultInspectionToolPresentation /** - * Overrides [resolveProblem] to avoid suppressing the button [ViewGeneratedTestFix]. + * Overrides [resolveProblem] to avoid suppressing quick fix buttons. */ class UTBotInspectionToolPresentation( toolWrapper: InspectionToolWrapper<*, *>, diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/ViewGeneratedTestFix.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/ViewGeneratedTestFix.kt index d017c30de9..d0c4605e76 100644 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/ViewGeneratedTestFix.kt +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/inspection/ViewGeneratedTestFix.kt @@ -22,6 +22,9 @@ class ViewGeneratedTestFix( val columnNumber: Int ) : LocalQuickFix { + /** + * Navigates the user to the [lineNumber] line of the [testFileRelativePath] file. + */ override fun applyFix(project: Project, descriptor: ProblemDescriptor) { val testFileAbsolutePath = project.basePath?.toPath()?.resolve(testFileRelativePath) ?: return val virtualFile = VfsUtil.findFile(testFileAbsolutePath, /* refreshIfNeeded = */ true) ?: return @@ -34,6 +37,9 @@ class ViewGeneratedTestFix( selectionModel.selectLineAtCaret() } + /** + * This text is displayed on the quick fix button. + */ override fun getName() = "View generated test" override fun getFamilyName() = name From 6fdef3cd6f30408a9513534ba816703fc499a775 Mon Sep 17 00:00:00 2001 From: Nikita Stroganov Date: Mon, 10 Oct 2022 12:49:16 +0300 Subject: [PATCH 05/10] Replace UTBot with UnitTestBot --- utbot-intellij/src/main/resources/META-INF/plugin.xml | 2 +- .../resources/inspectionDescriptions/UTBotInspectionTool.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/utbot-intellij/src/main/resources/META-INF/plugin.xml b/utbot-intellij/src/main/resources/META-INF/plugin.xml index bf9bb030a2..2d3969c2a3 100644 --- a/utbot-intellij/src/main/resources/META-INF/plugin.xml +++ b/utbot-intellij/src/main/resources/META-INF/plugin.xml @@ -90,7 +90,7 @@ -

Reports unchecked exceptions detected by UTBot.

+

Reports unchecked exceptions detected by UnitTestBot.

Example:

 void foo(int a) {

From 4c1eb24431141b94fe53f1d937b2c262b805181d Mon Sep 17 00:00:00 2001
From: Nikita Stroganov 
Date: Mon, 10 Oct 2022 12:53:39 +0300
Subject: [PATCH 06/10] Rebase onto main

---
 .../org/utbot/framework/process/EngineMain.kt |  14 +--
 .../generated/EngineProcessModel.Generated.kt |   7 +-
 .../generator/CodeGenerationController.kt     | 100 +++++++-----------
 .../intellij/plugin/process/EngineProcess.kt  |   8 +-
 .../intellij/plugin/sarif/SarifReportIdea.kt  |  73 ++++++++++---
 5 files changed, 116 insertions(+), 86 deletions(-)

diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineMain.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineMain.kt
index 16c888ddda..7a794f0dfb 100644
--- a/utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineMain.kt
+++ b/utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineMain.kt
@@ -175,13 +175,13 @@ private fun EngineProcessModel.setup(
     }
     synchronizer.measureExecutionForTermination(writeSarifReport) { params ->
         val reportFilePath = Paths.get(params.reportFilePath)
-        reportFilePath.toFile().writeText(
-            SarifReport(
-                testSets[params.testSetsId]!!,
-                params.generatedTestsCode,
-                RdSourceFindingStrategyFacade(realProtocol.rdSourceFindingStrategy)
-            ).createReport()
-        )
+        val sarifReportAsJson = SarifReport(
+            testSets[params.testSetsId]!!,
+            params.generatedTestsCode,
+            RdSourceFindingStrategyFacade(realProtocol.rdSourceFindingStrategy)
+        ).createReport().toJson()
+        reportFilePath.toFile().writeText(sarifReportAsJson)
+        sarifReportAsJson
     }
     synchronizer.measureExecutionForTermination(generateTestReport) { params ->
         val eventLogMessage = params.eventLogMessage
diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessModel.Generated.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessModel.Generated.kt
index 72ba022123..5c2332ee84 100644
--- a/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessModel.Generated.kt
+++ b/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessModel.Generated.kt
@@ -9,6 +9,7 @@ import com.jetbrains.rd.util.lifetime.*
 import com.jetbrains.rd.util.reactive.*
 import com.jetbrains.rd.util.string.*
 import com.jetbrains.rd.util.*
+import org.utbot.sarif.Sarif
 import kotlin.reflect.KClass
 import kotlin.jvm.JvmStatic
 
@@ -27,7 +28,7 @@ class EngineProcessModel private constructor(
     private val _obtainClassId: RdCall,
     private val _findMethodsInClassMatchingSelected: RdCall,
     private val _findMethodParamNames: RdCall,
-    private val _writeSarifReport: RdCall,
+    private val _writeSarifReport: RdCall,
     private val _generateTestReport: RdCall
 ) : RdExtBase() {
     //companion
@@ -89,7 +90,7 @@ class EngineProcessModel private constructor(
     val obtainClassId: RdCall get() = _obtainClassId
     val findMethodsInClassMatchingSelected: RdCall get() = _findMethodsInClassMatchingSelected
     val findMethodParamNames: RdCall get() = _findMethodParamNames
-    val writeSarifReport: RdCall get() = _writeSarifReport
+    val writeSarifReport: RdCall get() = _writeSarifReport
     val generateTestReport: RdCall get() = _generateTestReport
     //methods
     //initializer
@@ -133,7 +134,7 @@ class EngineProcessModel private constructor(
         RdCall(FrameworkMarshallers.String, FrameworkMarshallers.ByteArray),
         RdCall(FindMethodsInClassMatchingSelectedArguments, FindMethodsInClassMatchingSelectedResult),
         RdCall(FindMethodParamNamesArguments, FindMethodParamNamesResult),
-        RdCall(WriteSarifReportArguments, FrameworkMarshallers.Void),
+        RdCall(WriteSarifReportArguments, FrameworkMarshallers.String),
         RdCall(GenerateTestReportArgs, GenerateTestReportResult)
     )
     
diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/CodeGenerationController.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/CodeGenerationController.kt
index 2be935f2e8..702e95d228 100644
--- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/CodeGenerationController.kt
+++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/CodeGenerationController.kt
@@ -1,5 +1,6 @@
 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
@@ -41,8 +42,6 @@ import org.jetbrains.kotlin.psi.KtPsiFactory
 import org.jetbrains.kotlin.psi.psiUtil.endOffset
 import org.jetbrains.kotlin.psi.psiUtil.getChildrenOfType
 import org.jetbrains.kotlin.psi.psiUtil.startOffset
-import org.utbot.common.HTML_LINE_SEPARATOR
-import org.utbot.common.PathUtil.toHtmlLinkTag
 import org.utbot.framework.codegen.Import
 import org.utbot.framework.codegen.ParametrizedTestSource
 import org.utbot.framework.codegen.RegularImport
@@ -51,6 +50,7 @@ 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.UTBotInspectionManager
 import org.utbot.intellij.plugin.models.GenerateTestsModel
 import org.utbot.intellij.plugin.models.packageName
 import org.utbot.intellij.plugin.process.EngineProcess
@@ -65,13 +65,13 @@ 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.SarifReport
+import org.utbot.sarif.Sarif
 import java.nio.file.Path
 import java.util.concurrent.CountDownLatch
 import java.util.concurrent.TimeUnit
 
 object CodeGenerationController {
-    private val logger = KotlinLogging.logger {}
+    internal val logger = KotlinLogging.logger {}
 
     private class UtilClassListener {
         var requiredUtilClassKind: UtilClassKind? = null
@@ -92,6 +92,7 @@ object CodeGenerationController {
         val allTestPackages = getPackageDirectories(baseTestDirectory)
         val latch = CountDownLatch(classesWithTests.size)
         val testFilesPointers = mutableListOf>()
+        val srcClassPathToSarifReport = mutableMapOf()
         val utilClassListener = UtilClassListener()
         for ((srcClass, generateResult) in classesWithTests) {
             val (count, testSetsId) = generateResult
@@ -104,7 +105,7 @@ object CodeGenerationController {
                 val cut = psi2KClass[srcClass] ?: error("Didn't find KClass instance for class ${srcClass.name}")
                 runWriteCommandAction(model.project, "Generate tests with UtBot", null, {
                     try {
-                        generateCodeAndReport(proc, testSetsId, srcClass, cut, testClass, testFilePointer, model, latch, utilClassListener)
+                        generateCodeAndReport(proc, testSetsId, srcClass, cut, testClass, testFilePointer, model, latch, srcClassPathToSarifReport, utilClassListener)
                         testFilesPointers.add(testFilePointer)
                     } catch (e: IncorrectOperationException) {
                         logger.error { e }
@@ -127,7 +128,10 @@ object CodeGenerationController {
                             run(THREAD_POOL) {
                                 val sarifReportsPath =
                                     model.testModule.getOrCreateSarifReportsPath(model.testSourceRoot)
-                                mergeSarifReports(model, sarifReportsPath)
+
+                                SarifReportIdea.mergeSarifReports(model, sarifReportsPath)
+                                runInspectionsIfNeeded(model.project, srcClassPathToSarifReport)
+
                                 if (model.runGeneratedTestsWithCoverage) {
                                     RunConfigurationHelper.runTestsWithCoverage(model, testFilesPointers)
                                 }
@@ -140,6 +144,25 @@ object CodeGenerationController {
         }
     }
 
+    /**
+     * Runs the UTBot inspection if there are detected errors.
+     */
+    private fun runInspectionsIfNeeded(
+        project: Project,
+        srcClassPathToSarifReport: MutableMap
+    ) {
+        val sarifHasResults = srcClassPathToSarifReport.any { (_, sarif) ->
+            sarif.getAllResults().isNotEmpty()
+        }
+        if (!sarifHasResults) {
+            return
+        }
+        UTBotInspectionManager
+            .getInstance(project, srcClassPathToSarifReport)
+            .createNewGlobalContext()
+            .doInspections(AnalysisScope(project))
+    }
+
     private fun proceedTestReport(proc: EngineProcess, model: GenerateTestsModel) {
         try {
             // Parametrized tests are not supported in tests report yet
@@ -461,30 +484,6 @@ object CodeGenerationController {
         }
     }
 
-    private fun mergeSarifReports(model: GenerateTestsModel, sarifReportsPath: Path) {
-        val mergedReportFile = sarifReportsPath
-            .resolve("${model.project.name}Report.sarif")
-            .toFile()
-        // deleting the old report so that `sarifReports` does not contain it
-        mergedReportFile.delete()
-
-        val sarifReports = sarifReportsPath.toFile()
-            .walkTopDown()
-            .filter { it.extension == "sarif" }
-            .map { it.readText() }
-            .toList()
-
-        val mergedReport = SarifReport.mergeReports(sarifReports)
-        mergedReportFile.writeText(mergedReport)
-
-        // notifying the user
-        SarifReportNotifier.notify(
-            info = """
-                SARIF report was saved to ${toHtmlLinkTag(mergedReportFile.path)}$HTML_LINE_SEPARATOR
-            """.trimIndent()
-        )
-    }
-
     private fun getPackageDirectories(baseDirectory: PsiDirectory): Map {
         val allSubdirectories = mutableMapOf()
         getPackageDirectoriesRecursively(baseDirectory, allSubdirectories)
@@ -559,6 +558,7 @@ object CodeGenerationController {
         filePointer: SmartPsiElementPointer,
         model: GenerateTestsModel,
         reportsCountDown: CountDownLatch,
+        srcClassPathToSarifReport: MutableMap,
         utilClassListener: UtilClassListener
     ) {
         val classMethods = srcClass.extractClassMethodsIncludingNested(false)
@@ -611,16 +611,19 @@ object CodeGenerationController {
                             unblockDocument(testClassUpdated.project, editor.document)
 
                             // uploading formatted code
-                            val file = filePointer.containingFile
+                            val testsFile = filePointer.containingFile
 
-                            saveSarifReport(
+                            val sarifReport = SarifReportIdea.createAndSave(
                                 proc,
                                 testSetsId,
-                                testClassUpdated,
-                                classUnderTest,
+                                classUnderTest.name,
                                 model,
-                                file?.text?: generatedTestsCode
+                                testsFile?.text ?: generatedTestsCode,
+                                SourceFindingStrategyIdea(testClass)
                             )
+                            val srcClassPath = srcClass.containingFile.virtualFile.toNioPath()
+                            srcClassPathToSarifReport[srcClassPath] = sarifReport
+
                             unblockDocument(testClassUpdated.project, editor.document)
 
                             reportsCountDown.countDown()
@@ -650,33 +653,6 @@ object CodeGenerationController {
         }
     }
 
-    private fun saveSarifReport(
-        proc: EngineProcess,
-        testSetsId: Long,
-        testClass: PsiClass,
-        testClassId: ClassId,
-        model: GenerateTestsModel,
-        generatedTestsCode: String,
-    ) {
-        val project = model.project
-
-        try {
-            // saving sarif report
-            val sourceFinding = SourceFindingStrategyIdea(testClass)
-            executeCommand(testClass.project, "Saving Sarif report") {
-                SarifReportIdea.createAndSave(proc, testSetsId, testClassId, model, generatedTestsCode, sourceFinding)
-            }
-        } catch (e: Exception) {
-            logger.error { e }
-            showErrorDialogLater(
-                project,
-                message = "Cannot save Sarif report via generated tests: error occurred '${e.message}'",
-                title = "Failed to save Sarif report"
-            )
-        }
-    }
-
-
     private fun eventLogMessage(project: Project): String? {
         if (ToolWindowManager.getInstance(project).getToolWindow("Event Log") != null)
             return     """
diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt
index a8323b22f4..791464eba9 100644
--- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt
+++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt
@@ -36,6 +36,7 @@ import org.utbot.intellij.plugin.util.signature
 import org.utbot.rd.ProcessWithRdServer
 import org.utbot.rd.rdPortArgument
 import org.utbot.rd.startUtProcessWithRdServer
+import org.utbot.sarif.Sarif
 import org.utbot.sarif.SourceFindingStrategy
 import java.io.File
 import java.nio.file.Path
@@ -301,7 +302,7 @@ class EngineProcess(parent: Lifetime, val project: Project) {
                    testSetsId: Long,
                    generatedTestsCode: String,
                    sourceFindingStrategy: SourceFindingStrategy
-    ) = runBlocking {
+    ): Sarif = runBlocking {
         current!!.protocol.rdSourceFindingStrategy.let {
             it.getSourceFile.set { params ->
                 DumbService.getInstance(project).runReadActionInSmartMode {
@@ -325,7 +326,10 @@ class EngineProcess(parent: Lifetime, val project: Project) {
                 }
             }
         }
-        engineModel().writeSarifReport.startSuspending(WriteSarifReportArguments(testSetsId, reportFilePath.pathString, generatedTestsCode))
+        val sarifReportAsJson = engineModel().writeSarifReport.startSuspending(
+            WriteSarifReportArguments(testSetsId, reportFilePath.pathString, generatedTestsCode)
+        )
+        Sarif.fromJson(sarifReportAsJson)
     }
 
     fun generateTestsReport(model: GenerateTestsModel, eventLogMessage: String?): Triple = runBlocking {
diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/sarif/SarifReportIdea.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/sarif/SarifReportIdea.kt
index b7077863aa..3ce159f9a1 100644
--- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/sarif/SarifReportIdea.kt
+++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/sarif/SarifReportIdea.kt
@@ -1,12 +1,20 @@
 package org.utbot.intellij.plugin.sarif
 
-import org.utbot.common.PathUtil.classFqnToPath
-import org.utbot.intellij.plugin.ui.utils.getOrCreateSarifReportsPath
+import com.intellij.openapi.command.executeCommand
 import com.intellij.openapi.vfs.VfsUtil
 import org.jetbrains.kotlin.idea.util.application.runWriteAction
-import org.utbot.framework.plugin.api.ClassId
+import org.utbot.common.HTML_LINE_SEPARATOR
+import org.utbot.common.PathUtil
+import org.utbot.common.PathUtil.classFqnToPath
+import org.utbot.intellij.plugin.generator.CodeGenerationController
 import org.utbot.intellij.plugin.models.GenerateTestsModel
 import org.utbot.intellij.plugin.process.EngineProcess
+import org.utbot.intellij.plugin.ui.SarifReportNotifier
+import org.utbot.intellij.plugin.ui.utils.getOrCreateSarifReportsPath
+import org.utbot.intellij.plugin.ui.utils.showErrorDialogLater
+import org.utbot.sarif.Sarif
+import org.utbot.sarif.SarifReport
+import java.nio.file.Path
 
 object SarifReportIdea {
 
@@ -17,20 +25,61 @@ object SarifReportIdea {
     fun createAndSave(
         proc: EngineProcess,
         testSetsId: Long,
-        classId: ClassId,
+        srcClassFqn: String,
         model: GenerateTestsModel,
         generatedTestsCode: String,
         sourceFinding: SourceFindingStrategyIdea
-    ) {
-        // building the path to the report file
-        val classFqn = classId.name
-        val sarifReportsPath = model.testModule.getOrCreateSarifReportsPath(model.testSourceRoot)
-        val reportFilePath = sarifReportsPath.resolve("${classFqnToPath(classFqn)}Report.sarif")
+    ): Sarif {
+        var resultSarif = Sarif.empty()
+        try {
+            executeCommand(model.project, "Saving SARIF report") {
+                // building the path to the report file
+                val sarifReportsPath = model.testModule.getOrCreateSarifReportsPath(model.testSourceRoot)
+                val reportFilePath = sarifReportsPath.resolve("${classFqnToPath(srcClassFqn)}Report.sarif")
+
+                // creating report related directory
+                runWriteAction { VfsUtil.createDirectoryIfMissing(reportFilePath.parent.toString()) }
+
+                // creating & saving the report
+                resultSarif = proc.writeSarif(reportFilePath, testSetsId, generatedTestsCode, sourceFinding)
+            }
+        } catch (e: Exception) {
+            CodeGenerationController.logger.error { e }
+            showErrorDialogLater(
+                model.project,
+                message = "Cannot save Sarif report via generated tests: error occurred '${e.message}'",
+                title = "Failed to save Sarif report"
+            )
+        }
+        return resultSarif
+    }
+
+    /**
+     * Merges all SARIF reports into one large containing all the information.
+     * Saves it to the [sarifReportsPath] and notifies the user.
+     */
+    fun mergeSarifReports(model: GenerateTestsModel, sarifReportsPath: Path) {
+        val mergedReportFile = sarifReportsPath
+            .resolve("${model.project.name}Report.sarif")
+            .toFile()
+        // deleting the old report so that `sarifReports` does not contain it
+        mergedReportFile.delete()
+
+        val sarifReports = sarifReportsPath.toFile()
+            .walkTopDown()
+            .filter { it.extension == "sarif" }
+            .map { it.readText() }
+            .toList()
 
-        // creating report related directory
-        runWriteAction { VfsUtil.createDirectoryIfMissing(reportFilePath.parent.toString()) }
+        val mergedReport = SarifReport.mergeReports(sarifReports)
+        mergedReportFile.writeText(mergedReport)
 
-        proc.writeSarif(reportFilePath, testSetsId, generatedTestsCode, sourceFinding)
+        // notifying the user
+        SarifReportNotifier.notify(
+            info = """
+                SARIF report was saved to ${PathUtil.toHtmlLinkTag(mergedReportFile.path)}$HTML_LINE_SEPARATOR
+            """.trimIndent()
+        )
     }
 }
 

From 37eeeae63a104444161a44bf4203ad9cd6292ab9 Mon Sep 17 00:00:00 2001
From: Nikita Stroganov 
Date: Mon, 10 Oct 2022 16:57:25 +0300
Subject: [PATCH 07/10] Add runInspectionAfterTestGeneration to settings

---
 .../intellij/plugin/generator/CodeGenerationController.kt   | 4 ++++
 .../kotlin/org/utbot/intellij/plugin/settings/Settings.kt   | 6 ++++++
 2 files changed, 10 insertions(+)

diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/CodeGenerationController.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/CodeGenerationController.kt
index 702e95d228..1db75e7cff 100644
--- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/CodeGenerationController.kt
+++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/CodeGenerationController.kt
@@ -12,6 +12,7 @@ import com.intellij.openapi.application.runReadAction
 import com.intellij.openapi.application.runWriteAction
 import com.intellij.openapi.command.WriteCommandAction.runWriteCommandAction
 import com.intellij.openapi.command.executeCommand
+import com.intellij.openapi.components.service
 import com.intellij.openapi.editor.Document
 import com.intellij.openapi.editor.Editor
 import com.intellij.openapi.fileEditor.FileDocumentManager
@@ -57,6 +58,7 @@ 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.settings.Settings
 import org.utbot.intellij.plugin.ui.*
 import org.utbot.intellij.plugin.ui.utils.getOrCreateSarifReportsPath
 import org.utbot.intellij.plugin.ui.utils.showErrorDialogLater
@@ -151,6 +153,8 @@ object CodeGenerationController {
         project: Project,
         srcClassPathToSarifReport: MutableMap
     ) {
+        if (!project.service().runInspectionAfterTestGeneration)
+            return
         val sarifHasResults = srcClassPathToSarifReport.any { (_, sarif) ->
             sarif.getAllResults().isNotEmpty()
         }
diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/settings/Settings.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/settings/Settings.kt
index 9c83a33a15..1ebfc80536 100644
--- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/settings/Settings.kt
+++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/settings/Settings.kt
@@ -55,6 +55,7 @@ class Settings(val project: Project) : PersistentStateComponent
         var classesToMockAlways: Array = Mocker.defaultSuperClassesToMockAlwaysNames.toTypedArray(),
         var fuzzingValue: Double = 0.05,
         var runGeneratedTestsWithCoverage: Boolean = false,
+        var runInspectionAfterTestGeneration: Boolean = true,
         var commentStyle: JavaDocCommentStyle = JavaDocCommentStyle.defaultItem
     ) {
         constructor(model: GenerateTestsModel) : this(
@@ -92,6 +93,7 @@ class Settings(val project: Project) : PersistentStateComponent
             if (!classesToMockAlways.contentEquals(other.classesToMockAlways)) return false
             if (fuzzingValue != other.fuzzingValue) return false
             if (runGeneratedTestsWithCoverage != other.runGeneratedTestsWithCoverage) return false
+            if (runInspectionAfterTestGeneration != other.runInspectionAfterTestGeneration) return false
 
             return true
         }
@@ -109,6 +111,7 @@ class Settings(val project: Project) : PersistentStateComponent
             result = 31 * result + classesToMockAlways.contentHashCode()
             result = 31 * result + fuzzingValue.hashCode()
             result = 31 * result + if (runGeneratedTestsWithCoverage) 1 else 0
+            result = 31 * result + if (runInspectionAfterTestGeneration) 1 else 0
 
             return result
         }
@@ -147,8 +150,11 @@ class Settings(val project: Project) : PersistentStateComponent
         set(value) {
             state.fuzzingValue = value.coerceIn(0.0, 1.0)
         }
+
     var runGeneratedTestsWithCoverage = state.runGeneratedTestsWithCoverage
 
+    val runInspectionAfterTestGeneration = state.runInspectionAfterTestGeneration
+
     fun setClassesToMockAlways(classesToMockAlways: List) {
         state.classesToMockAlways = classesToMockAlways.distinct().toTypedArray()
     }

From 8005547b49b54907410ba8667d52ec772856e657 Mon Sep 17 00:00:00 2001
From: Nikita Stroganov 
Date: Mon, 10 Oct 2022 20:21:41 +0300
Subject: [PATCH 08/10] Debug commit

---
 .../kotlin/org/utbot/framework/UtSettings.kt  |  2 +-
 .../org/utbot/framework/process/EngineMain.kt | 13 +++-
 .../generated/EngineProcessModel.Generated.kt |  3 +-
 .../utbot/sarif/RdSourceFindingStrategy.kt    |  4 ++
 .../generator/CodeGenerationController.kt     | 66 ++++++++++++-------
 .../intellij/plugin/sarif/SarifReportIdea.kt  | 11 +++-
 .../plugin/sarif/SourceFindingStrategyIdea.kt | 17 +++--
 .../org/utbot/rd/models/EngineProcessModel.kt |  2 +-
 8 files changed, 85 insertions(+), 33 deletions(-)

diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt
index 8ce3cf15ee..7432377850 100644
--- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt
+++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/UtSettings.kt
@@ -279,7 +279,7 @@ object UtSettings : AbstractSettings(
      *
      * False by default (for saving disk space).
      */
-    var logConcreteExecutionErrors by getBooleanProperty(false)
+    var logConcreteExecutionErrors by getBooleanProperty(true)
 
     /**
      * Number of branch instructions using for clustering executions in the test minimization phase.
diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineMain.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineMain.kt
index 7a794f0dfb..ac90dd2e9e 100644
--- a/utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineMain.kt
+++ b/utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineMain.kt
@@ -32,6 +32,7 @@ import org.utbot.rd.findRdPort
 import org.utbot.rd.loggers.UtRdKLoggerFactory
 import org.utbot.sarif.RdSourceFindingStrategyFacade
 import org.utbot.sarif.SarifReport
+import org.utbot.sarif.SourceFindingStrategyDefault
 import org.utbot.summary.summarize
 import soot.SootMethod
 import soot.UnitPatchingChain
@@ -174,13 +175,23 @@ private fun EngineProcessModel.setup(
         ))
     }
     synchronizer.measureExecutionForTermination(writeSarifReport) { params ->
+        val lg = org.utbot.framework.process.logger
+        lg.error("START")
         val reportFilePath = Paths.get(params.reportFilePath)
+        repeat(10) { lg.error("EEEEEEEEEEEEEEEEEEEEEEEEEEE") }
+        lg.error(reportFilePath.toString()); repeat(5) { lg.error("\n") }
+        lg.error(testSets[params.testSetsId].toString()); repeat(5) { lg.error("\n") }
+        lg.error(params.generatedTestsCode); repeat(5) { lg.error("\n") }
+        val rdSourceFindingStrategy = RdSourceFindingStrategyFacade(realProtocol.rdSourceFindingStrategy)
+        lg.error(rdSourceFindingStrategy.toString()); repeat(5) { lg.error("\n") }
         val sarifReportAsJson = SarifReport(
             testSets[params.testSetsId]!!,
             params.generatedTestsCode,
-            RdSourceFindingStrategyFacade(realProtocol.rdSourceFindingStrategy)
+            rdSourceFindingStrategy
         ).createReport().toJson()
+        lg.error(sarifReportAsJson); repeat(5) { lg.error("\n") }
         reportFilePath.toFile().writeText(sarifReportAsJson)
+        lg.error("Success!")
         sarifReportAsJson
     }
     synchronizer.measureExecutionForTermination(generateTestReport) { params ->
diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessModel.Generated.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessModel.Generated.kt
index 5c2332ee84..4fb9685f59 100644
--- a/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessModel.Generated.kt
+++ b/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessModel.Generated.kt
@@ -9,7 +9,6 @@ import com.jetbrains.rd.util.lifetime.*
 import com.jetbrains.rd.util.reactive.*
 import com.jetbrains.rd.util.string.*
 import com.jetbrains.rd.util.*
-import org.utbot.sarif.Sarif
 import kotlin.reflect.KClass
 import kotlin.jvm.JvmStatic
 
@@ -74,7 +73,7 @@ class EngineProcessModel private constructor(
         }
         
         
-        const val serializationHash = 4674749231408610997L
+        const val serializationHash = 1019152229801641560L
         
     }
     override val serializersOwner: ISerializersOwner get() = EngineProcessModel
diff --git a/utbot-framework/src/main/kotlin/org/utbot/sarif/RdSourceFindingStrategy.kt b/utbot-framework/src/main/kotlin/org/utbot/sarif/RdSourceFindingStrategy.kt
index 68adbb1295..33eefbc6d5 100644
--- a/utbot-framework/src/main/kotlin/org/utbot/sarif/RdSourceFindingStrategy.kt
+++ b/utbot-framework/src/main/kotlin/org/utbot/sarif/RdSourceFindingStrategy.kt
@@ -1,6 +1,7 @@
 package org.utbot.sarif
 
 import kotlinx.coroutines.runBlocking
+import org.utbot.engine.logger
 import org.utbot.framework.process.generated.RdSourceFindingStrategy
 import org.utbot.framework.process.generated.SourceStrategeMethodArgs
 import java.io.File
@@ -10,11 +11,14 @@ class RdSourceFindingStrategyFacade(private val realStrategy: RdSourceFindingStr
         get() = runBlocking { realStrategy.testsRelativePath.startSuspending(Unit) }
 
     override fun getSourceRelativePath(classFqn: String, extension: String?): String = runBlocking {
+        logger.error("getSourceRelativePath for $classFqn, $extension")
         realStrategy.getSourceRelativePath.startSuspending(SourceStrategeMethodArgs(classFqn, extension))
     }
 
     override fun getSourceFile(classFqn: String, extension: String?): File? = runBlocking {
+        logger.error("getSourceFile for $classFqn, $extension")
         realStrategy.getSourceFile.startSuspending(SourceStrategeMethodArgs(classFqn, extension))?.let {
+            logger.error("it: $it")
             File(it)
         }
     }
diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/CodeGenerationController.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/CodeGenerationController.kt
index 1db75e7cff..2e6ec25700 100644
--- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/CodeGenerationController.kt
+++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/CodeGenerationController.kt
@@ -57,7 +57,6 @@ 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.settings.Settings
 import org.utbot.intellij.plugin.ui.*
 import org.utbot.intellij.plugin.ui.utils.getOrCreateSarifReportsPath
@@ -103,11 +102,14 @@ object CodeGenerationController {
                 val classPackageName = model.getTestClassPackageNameFor(srcClass)
                 val testDirectory = allTestPackages[classPackageName] ?: baseTestDirectory
                 val testClass = createTestClass(srcClass, testDirectory, model) ?: continue
-                val testFilePointer = SmartPointerManager.getInstance(model.project).createSmartPsiElementPointer(testClass.containingFile)
+
+                val smartPointerManager = SmartPointerManager.getInstance(model.project)
+                val testClassPointer = smartPointerManager.createSmartPsiElementPointer(testClass)
+                val testFilePointer = smartPointerManager.createSmartPsiElementPointer(testClass.containingFile)
                 val cut = psi2KClass[srcClass] ?: error("Didn't find KClass instance for class ${srcClass.name}")
                 runWriteCommandAction(model.project, "Generate tests with UtBot", null, {
                     try {
-                        generateCodeAndReport(proc, testSetsId, srcClass, cut, testClass, testFilePointer, model, latch, srcClassPathToSarifReport, utilClassListener)
+                        generateCodeAndReport(proc, testSetsId, srcClass, cut, testClassPointer, testFilePointer, model, latch, srcClassPathToSarifReport, utilClassListener)
                         testFilesPointers.add(testFilePointer)
                     } catch (e: IncorrectOperationException) {
                         logger.error { e }
@@ -283,7 +285,12 @@ object CodeGenerationController {
         }
 
         runWriteCommandAction(model.project, "UtBot util class reformatting", null, {
-            reformat(model, SmartPointerManager.getInstance(model.project).createSmartPsiElementPointer(utUtilsFile), utUtilsClass)
+            val smartPointerManager = SmartPointerManager.getInstance(model.project)
+            reformat(
+                model,
+                smartPointerManager.createSmartPsiElementPointer(utUtilsFile),
+                smartPointerManager.createSmartPsiElementPointer(utUtilsClass)
+            )
         })
 
         val utUtilsDocument = runReadAction {
@@ -558,8 +565,8 @@ object CodeGenerationController {
         testSetsId: Long,
         srcClass: PsiClass,
         classUnderTest: ClassId,
-        testClass: PsiClass,
-        filePointer: SmartPsiElementPointer,
+        testClassPointer: SmartPsiElementPointer,
+        testFilePointer: SmartPsiElementPointer,
         model: GenerateTestsModel,
         reportsCountDown: CountDownLatch,
         srcClassPathToSarifReport: MutableMap,
@@ -568,8 +575,13 @@ object CodeGenerationController {
         val classMethods = srcClass.extractClassMethodsIncludingNested(false)
         val paramNames = DumbService.getInstance(model.project)
             .runReadActionInSmartMode(Computable { proc.findMethodParamNames(classUnderTest, classMethods) })
-        val testPackageName = testClass.packageName
-        val editor = CodeInsightUtil.positionCursorAtLBrace(testClass.project, filePointer.containingFile, testClass)
+        val testPackageName = testClassPointer.element?.packageName
+            ?: return // TODO
+        val editor = CodeInsightUtil.positionCursorAtLBrace(
+            testClassPointer.project,
+            testFilePointer.containingFile,
+            testClassPointer.element ?: return // TODO
+        )
         //TODO: Use PsiDocumentManager.getInstance(model.project).getDocument(file)
         // if we don't want to open _all_ new files with tests in editor one-by-one
         run(THREAD_POOL) {
@@ -593,42 +605,42 @@ object CodeGenerationController {
             utilClassListener.onTestClassGenerated(utilClassKind)
             run(EDT_LATER) {
                 run(WRITE_ACTION) {
-                    unblockDocument(testClass.project, editor.document)
+                    unblockDocument(testClassPointer.project, editor.document)
                     // TODO: JIRA:1246 - display warnings if we rewrite the file
-                    executeCommand(testClass.project, "Insert Generated Tests") {
+                    executeCommand(testClassPointer.project, "Insert Generated Tests") {
                         editor.document.setText(generatedTestsCode.replace("jdk.internal.misc.Unsafe", "sun.misc.Unsafe"))
                     }
-                    unblockDocument(testClass.project, editor.document)
+                    unblockDocument(testClassPointer.project, editor.document)
 
                     // after committing the document the `testClass` is invalid in PsiTree,
                     // so we have to reload it from the corresponding `file`
-                    val testClassUpdated = (filePointer.containingFile as PsiClassOwner).classes.first() // only one class in the file
+//                    val testClassUpdated = (testFilePointer.containingFile as PsiClassOwner).classes.first() // only one class in the file
 
                     // reformatting before creating reports due to
                     // SarifReport requires the final version of the generated tests code
                     run(THREAD_POOL) {
 //                        IntentionHelper(model.project, editor, filePointer).applyIntentions()
                         run(EDT_LATER) {
-                            runWriteCommandAction(filePointer.project, "UtBot tests reformatting", null, {
-                                reformat(model, filePointer, testClassUpdated)
+                            runWriteCommandAction(testFilePointer.project, "UtBot tests reformatting", null, {
+                                reformat(model, testFilePointer, testClassPointer)
                             })
-                            unblockDocument(testClassUpdated.project, editor.document)
+                            unblockDocument(testClassPointer.project, editor.document)
 
-                            // uploading formatted code
-                            val testsFile = filePointer.containingFile
+//                            // uploading formatted code
+//                            val testsFile = testFilePointer.containingFile
 
                             val sarifReport = SarifReportIdea.createAndSave(
                                 proc,
                                 testSetsId,
                                 classUnderTest.name,
                                 model,
-                                testsFile?.text ?: generatedTestsCode,
-                                SourceFindingStrategyIdea(testClass)
+                                testClassPointer,
+                                testFilePointer.element?.text ?: generatedTestsCode,
                             )
                             val srcClassPath = srcClass.containingFile.virtualFile.toNioPath()
                             srcClassPathToSarifReport[srcClassPath] = sarifReport
 
-                            unblockDocument(testClassUpdated.project, editor.document)
+                            unblockDocument(testClassPointer.project, editor.document)
 
                             reportsCountDown.countDown()
                         }
@@ -638,10 +650,14 @@ object CodeGenerationController {
         }
     }
 
-    private fun reformat(model: GenerateTestsModel, smartPointer: SmartPsiElementPointer, testClass: PsiClass) {
+    private fun reformat(
+        model: GenerateTestsModel,
+        testFilePointer: SmartPsiElementPointer,
+        testClassPointer: SmartPsiElementPointer,
+    ) {
         val project = model.project
         val codeStyleManager = CodeStyleManager.getInstance(project)
-        val file = smartPointer.containingFile?: return
+        val file = testFilePointer.containingFile?: return
         DumbService.getInstance(model.project).runWhenSmart {
             codeStyleManager.reformat(file)
             when (model.codegenLanguage) {
@@ -652,7 +668,11 @@ object CodeGenerationController {
                     val reformatRange = codeStyleManager.reformatRange(file, startOffset, endOffset, false)
                     JavaCodeStyleManager.getInstance(project).shortenClassReferences(reformatRange)
                 }
-                CodegenLanguage.KOTLIN -> ShortenReferences.DEFAULT.process((testClass as KtUltraLightClass).kotlinOrigin.containingKtFile)
+                CodegenLanguage.KOTLIN -> {
+                    ShortenReferences.DEFAULT.process(
+                        (testClassPointer.element as KtUltraLightClass).kotlinOrigin.containingKtFile
+                    )
+                }
             }
         }
     }
diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/sarif/SarifReportIdea.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/sarif/SarifReportIdea.kt
index 3ce159f9a1..3446da0f20 100644
--- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/sarif/SarifReportIdea.kt
+++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/sarif/SarifReportIdea.kt
@@ -2,6 +2,8 @@ package org.utbot.intellij.plugin.sarif
 
 import com.intellij.openapi.command.executeCommand
 import com.intellij.openapi.vfs.VfsUtil
+import com.intellij.psi.PsiClass
+import com.intellij.psi.SmartPsiElementPointer
 import org.jetbrains.kotlin.idea.util.application.runWriteAction
 import org.utbot.common.HTML_LINE_SEPARATOR
 import org.utbot.common.PathUtil
@@ -14,6 +16,7 @@ import org.utbot.intellij.plugin.ui.utils.getOrCreateSarifReportsPath
 import org.utbot.intellij.plugin.ui.utils.showErrorDialogLater
 import org.utbot.sarif.Sarif
 import org.utbot.sarif.SarifReport
+import org.utbot.sarif.SourceFindingStrategyDefault
 import java.nio.file.Path
 
 object SarifReportIdea {
@@ -27,8 +30,8 @@ object SarifReportIdea {
         testSetsId: Long,
         srcClassFqn: String,
         model: GenerateTestsModel,
+        testClassPointer: SmartPsiElementPointer,
         generatedTestsCode: String,
-        sourceFinding: SourceFindingStrategyIdea
     ): Sarif {
         var resultSarif = Sarif.empty()
         try {
@@ -41,6 +44,12 @@ object SarifReportIdea {
                 runWriteAction { VfsUtil.createDirectoryIfMissing(reportFilePath.parent.toString()) }
 
                 // creating & saving the report
+                val sourceFinding = SourceFindingStrategyDefault(
+                    "io.github.ideaseeker.Main",
+                    "C:/Users/sWX1137517/IdeaProjects/SarifTest/src/main/java/io/github/ideaseeker/Main.java",
+                    "C:/Users/sWX1137517/IdeaProjects/SarifTest/src/test/java/io/github/ideaseeker/MainTest.java",
+                    "C:/Users/sWX1137517/IdeaProjects/SarifTest/",
+                ) //SourceFindingStrategyIdea(testClassPointer)
                 resultSarif = proc.writeSarif(reportFilePath, testSetsId, generatedTestsCode, sourceFinding)
             }
         } catch (e: Exception) {
diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/sarif/SourceFindingStrategyIdea.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/sarif/SourceFindingStrategyIdea.kt
index 456cc749fe..c925ee44ff 100644
--- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/sarif/SourceFindingStrategyIdea.kt
+++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/sarif/SourceFindingStrategyIdea.kt
@@ -2,6 +2,7 @@ package org.utbot.intellij.plugin.sarif
 
 import com.intellij.psi.JavaPsiFacade
 import com.intellij.psi.PsiClass
+import com.intellij.psi.SmartPsiElementPointer
 import org.jetbrains.kotlin.idea.search.allScope
 import org.utbot.common.PathUtil.classFqnToPath
 import org.utbot.common.PathUtil.safeRelativize
@@ -12,7 +13,9 @@ import java.io.File
 /**
  * The search strategy based on the information available to the PsiClass.
  */
-class SourceFindingStrategyIdea(testClass: PsiClass) : SourceFindingStrategy() {
+class SourceFindingStrategyIdea(
+    testClassPointer: SmartPsiElementPointer
+) : SourceFindingStrategy() {
 
     /**
      * Returns the relative path (against `project.basePath`) to the file with generated tests.
@@ -44,15 +47,21 @@ class SourceFindingStrategyIdea(testClass: PsiClass) : SourceFindingStrategy() {
 
     // internal
 
-    private val project = testClass.project
+    private val project by lazy {
+        testClassPointer.project
+    }
 
-    private val testsFilePath = testClass.containingFile.virtualFile.path
+    private val testsFilePath by lazy {
+        testClassPointer.virtualFile.path
+    }
 
     /**
      * The file extension to be used in [getSourceRelativePath] if the source file
      * was not found by the class qualified name and the `extension` parameter is null.
      */
-    private val defaultExtension = "." + (testClass.containingFile.virtualFile.extension ?: "java")
+    private val defaultExtension by lazy {
+        "." + (testClassPointer.virtualFile.extension ?: "java")
+    }
 
     /**
      * Returns PsiClass by given [classFqn].
diff --git a/utbot-rd/src/main/rdgen/org/utbot/rd/models/EngineProcessModel.kt b/utbot-rd/src/main/rdgen/org/utbot/rd/models/EngineProcessModel.kt
index d54c9d31ee..315be52298 100644
--- a/utbot-rd/src/main/rdgen/org/utbot/rd/models/EngineProcessModel.kt
+++ b/utbot-rd/src/main/rdgen/org/utbot/rd/models/EngineProcessModel.kt
@@ -122,7 +122,7 @@ object EngineProcessModel : Ext(EngineProcessProtocolRoot) {
         call("obtainClassId", PredefinedType.string, array(PredefinedType.byte)).async
         call("findMethodsInClassMatchingSelected", findMethodsInClassMatchingSelectedArguments, findMethodsInClassMatchingSelectedResult).async
         call("findMethodParamNames", findMethodParamNamesArguments, findMethodParamNamesResult).async
-        call("writeSarifReport", writeSarifReportArguments, PredefinedType.void).async
+        call("writeSarifReport", writeSarifReportArguments, PredefinedType.string).async
         call("generateTestReport", generateTestReportArgs, generateTestReportResult).async
     }
 }
\ No newline at end of file

From 771e53de234e0ebcbb98aefe5145130a644165af Mon Sep 17 00:00:00 2001
From: Nikita Stroganov 
Date: Wed, 12 Oct 2022 09:41:53 +0300
Subject: [PATCH 09/10] Debug commit 2

---
 .../org/utbot/framework/process/EngineMain.kt       | 13 ++++++++++---
 1 file changed, 10 insertions(+), 3 deletions(-)

diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineMain.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineMain.kt
index ac90dd2e9e..819f786ef8 100644
--- a/utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineMain.kt
+++ b/utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineMain.kt
@@ -182,12 +182,19 @@ private fun EngineProcessModel.setup(
         lg.error(reportFilePath.toString()); repeat(5) { lg.error("\n") }
         lg.error(testSets[params.testSetsId].toString()); repeat(5) { lg.error("\n") }
         lg.error(params.generatedTestsCode); repeat(5) { lg.error("\n") }
-        val rdSourceFindingStrategy = RdSourceFindingStrategyFacade(realProtocol.rdSourceFindingStrategy)
-        lg.error(rdSourceFindingStrategy.toString()); repeat(5) { lg.error("\n") }
+//        val rdSourceFindingStrategy = RdSourceFindingStrategyFacade(realProtocol.rdSourceFindingStrategy)
+        val sfs = SourceFindingStrategyDefault(
+            "io.github.ideaseeker.Main",
+            "C:/Users/sWX1137517/IdeaProjects/SarifTest/src/main/java/io/github/ideaseeker/Main.java",
+            "C:/Users/sWX1137517/IdeaProjects/SarifTest/src/test/java/io/github/ideaseeker/MainTest.java",
+            "C:/Users/sWX1137517/IdeaProjects/SarifTest/",
+        )
+//        lg.error(rdSourceFindingStrategy.toString()); repeat(5) { lg.error("\n") }
         val sarifReportAsJson = SarifReport(
             testSets[params.testSetsId]!!,
             params.generatedTestsCode,
-            rdSourceFindingStrategy
+            sfs
+//            rdSourceFindingStrategy
         ).createReport().toJson()
         lg.error(sarifReportAsJson); repeat(5) { lg.error("\n") }
         reportFilePath.toFile().writeText(sarifReportAsJson)

From 75ed6632bcda430e6847906e9165221ffc6359bd Mon Sep 17 00:00:00 2001
From: Nikita Stroganov 
Date: Thu, 13 Oct 2022 11:28:52 +0300
Subject: [PATCH 10/10] Add .rdgen to .gitignore

---
 .gitignore | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.gitignore b/.gitignore
index 7db9331a69..aa105a981b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,3 +7,4 @@ target/
 .idea/
 .gradle/
 *.log
+.rdgen