Skip to content

Commit 8aecd99

Browse files
committed
The first version of the utbot inspection tool (#972)
1 parent dc586fa commit 8aecd99

File tree

11 files changed

+261
-52
lines changed

11 files changed

+261
-52
lines changed

utbot-cli/src/main/kotlin/org/utbot/cli/GenerateTestsCommand.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ class GenerateTestsCommand :
149149
else -> {
150150
val sourceFinding =
151151
SourceFindingStrategyDefault(classFqn, sourceCodeFile, testsFilePath, projectRootPath)
152-
val report = SarifReport(testSets, testClassBody, sourceFinding).createReport()
152+
val report = SarifReport(testSets, testClassBody, sourceFinding).createReport().toJson()
153153
saveToFile(report, sarifReport)
154154
println("The report was saved to \"$sarifReport\".")
155155
}

utbot-framework-test/src/test/kotlin/org/utbot/sarif/SarifReportTest.kt

+12-16
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package org.utbot.sarif
22

3-
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
4-
import com.fasterxml.jackson.module.kotlin.readValue
53
import org.junit.Test
64
import org.mockito.Mockito
75
import org.utbot.framework.plugin.api.ExecutableId
@@ -19,7 +17,7 @@ class SarifReportTest {
1917
testSets = listOf(),
2018
generatedTestsCode = "",
2119
sourceFindingEmpty
22-
).createReport()
20+
).createReport().toJson()
2321

2422
assert(actualReport.isNotEmpty())
2523
}
@@ -30,7 +28,7 @@ class SarifReportTest {
3028
testSets = listOf(testSet),
3129
generatedTestsCode = "",
3230
sourceFindingEmpty
33-
).createReport().toSarif()
31+
).createReport()
3432

3533
assert(sarif.runs.first().results.isEmpty())
3634
}
@@ -60,7 +58,7 @@ class SarifReportTest {
6058
testSets = testSets,
6159
generatedTestsCode = "",
6260
sourceFindingEmpty
63-
).createReport().toSarif()
61+
).createReport()
6462

6563
assert(report.runs.first().results[0].message.text.contains("NullPointerException"))
6664
assert(report.runs.first().results[1].message.text.contains("ArrayIndexOutOfBoundsException"))
@@ -77,7 +75,7 @@ class SarifReportTest {
7775
Mockito.`when`(mockUtExecution.path.lastOrNull()?.stmt?.javaSourceStartLineNumber).thenReturn(1337)
7876
Mockito.`when`(mockUtExecution.testMethodName).thenReturn("testMain_ThrowArithmeticException")
7977

80-
val report = sarifReportMain.createReport().toSarif()
78+
val report = sarifReportMain.createReport()
8179

8280
val result = report.runs.first().results.first()
8381
val location = result.locations.first().physicalLocation
@@ -105,7 +103,7 @@ class SarifReportTest {
105103
)
106104
)
107105

108-
val report = sarifReportMain.createReport().toSarif()
106+
val report = sarifReportMain.createReport()
109107

110108
val result = report.runs.first().results.first()
111109
assert(result.message.text.contains("227"))
@@ -128,7 +126,7 @@ class SarifReportTest {
128126
)
129127
Mockito.`when`(mockUtExecution.stateBefore.parameters).thenReturn(listOf())
130128

131-
val report = sarifReportMain.createReport().toSarif()
129+
val report = sarifReportMain.createReport()
132130

133131
val result = report.runs.first().results.first().codeFlows.first().threadFlows.first().locations.map {
134132
it.location.physicalLocation
@@ -153,7 +151,7 @@ class SarifReportTest {
153151
Mockito.`when`(mockUtExecution.stateBefore.parameters).thenReturn(listOf())
154152
Mockito.`when`(mockUtExecution.testMethodName).thenReturn("testMain_ThrowArithmeticException")
155153

156-
val report = sarifReportMain.createReport().toSarif()
154+
val report = sarifReportMain.createReport()
157155

158156
val codeFlowPhysicalLocations = report.runs[0].results[0].codeFlows[0].threadFlows[0].locations.map {
159157
it.location.physicalLocation
@@ -177,7 +175,7 @@ class SarifReportTest {
177175
Mockito.`when`(mockUtExecution.stateBefore.parameters).thenReturn(listOf())
178176
Mockito.`when`(mockUtExecution.testMethodName).thenReturn("testMain_ThrowArithmeticException")
179177

180-
val report = sarifReportPrivateMain.createReport().toSarif()
178+
val report = sarifReportPrivateMain.createReport()
181179

182180
val codeFlowPhysicalLocations = report.runs[0].results[0].codeFlows[0].threadFlows[0].locations.map {
183181
it.location.physicalLocation
@@ -203,7 +201,7 @@ class SarifReportTest {
203201
testSets = testSets,
204202
generatedTestsCode = "",
205203
sourceFindingMain
206-
).createReport().toSarif()
204+
).createReport()
207205

208206
assert(report.runs.first().results.size == 1) // no duplicates
209207
}
@@ -228,7 +226,7 @@ class SarifReportTest {
228226
testSets = testSets,
229227
generatedTestsCode = "",
230228
sourceFindingMain
231-
).createReport().toSarif()
229+
).createReport()
232230

233231
assert(report.runs.first().results.size == 2) // no results have been removed
234232
}
@@ -257,7 +255,7 @@ class SarifReportTest {
257255
testSets = testSets,
258256
generatedTestsCode = "",
259257
sourceFindingMain
260-
).createReport().toSarif()
258+
).createReport()
261259

262260
assert(report.runs.first().results.size == 2) // no results have been removed
263261
}
@@ -291,7 +289,7 @@ class SarifReportTest {
291289
testSets = testSets,
292290
generatedTestsCode = "",
293291
sourceFindingMain
294-
).createReport().toSarif()
292+
).createReport()
295293

296294
assert(report.runs.first().results.size == 1) // no duplicates
297295
assert(report.runs.first().results.first().totalCodeFlowLocations() == 1) // with a shorter stack trace
@@ -310,8 +308,6 @@ class SarifReportTest {
310308
Mockito.`when`(mockExecutableId.classId.name).thenReturn("Main")
311309
}
312310

313-
private fun String.toSarif(): Sarif = jacksonObjectMapper().readValue(this)
314-
315311
// constants
316312

317313
private val sourceFindingEmpty = SourceFindingStrategyDefault(

utbot-framework/src/main/kotlin/org/utbot/framework/plugin/sarif/GenerateTestsAndSarifReportFacade.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ class GenerateTestsAndSarifReportFacade(
9191
testClassBody: String,
9292
sourceFinding: SourceFindingStrategy
9393
) {
94-
val sarifReport = SarifReport(testSets, testClassBody, sourceFinding).createReport()
94+
val sarifReport = SarifReport(testSets, testClassBody, sourceFinding).createReport().toJson()
9595
targetClass.sarifReportFile.writeText(sarifReport)
9696
}
9797
}

utbot-framework/src/main/kotlin/org/utbot/sarif/DataClasses.kt

+17-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
package org.utbot.sarif
22

3+
import com.fasterxml.jackson.annotation.JsonInclude
34
import com.fasterxml.jackson.annotation.JsonProperty
45
import com.fasterxml.jackson.annotation.JsonValue
6+
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
7+
import com.fasterxml.jackson.module.kotlin.readValue
58

69
/**
710
* Useful links:
@@ -24,7 +27,19 @@ data class Sarif(
2427

2528
fun fromRun(run: SarifRun) =
2629
Sarif(defaultSchema, defaultVersion, listOf(run))
30+
31+
fun fromJson(reportInJson: String): Sarif =
32+
jacksonObjectMapper().readValue(reportInJson)
2733
}
34+
35+
fun toJson(): String =
36+
jacksonObjectMapper()
37+
.setSerializationInclusion(JsonInclude.Include.NON_NULL)
38+
.writerWithDefaultPrettyPrinter()
39+
.writeValueAsString(this)
40+
41+
fun getAllResults(): List<SarifResult> =
42+
runs.flatMap { it.results }
2843
}
2944

3045
/**
@@ -104,8 +119,8 @@ data class SarifResult(
104119
* Returns the total number of locations in all [codeFlows].
105120
*/
106121
fun totalCodeFlowLocations() =
107-
codeFlows.sumBy { codeFlow ->
108-
codeFlow.threadFlows.sumBy { threadFlow ->
122+
codeFlows.sumOf { codeFlow ->
123+
codeFlow.threadFlows.sumOf { threadFlow ->
109124
threadFlow.locations.size
110125
}
111126
}

utbot-framework/src/main/kotlin/org/utbot/sarif/SarifReport.kt

+13-32
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
package org.utbot.sarif
22

3-
import com.fasterxml.jackson.annotation.JsonInclude
4-
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
5-
import com.fasterxml.jackson.module.kotlin.readValue
63
import org.utbot.common.PathUtil.fileExtension
74
import org.utbot.common.PathUtil.toPath
85
import org.utbot.framework.UtSettings
@@ -21,45 +18,20 @@ class SarifReport(
2118
private val generatedTestsCode: String,
2219
private val sourceFinding: SourceFindingStrategy
2320
) {
24-
2521
companion object {
26-
2722
/**
2823
* Merges several SARIF reports given as JSON-strings into one
2924
*/
3025
fun mergeReports(reports: List<String>): String =
3126
reports.fold(Sarif.empty()) { sarif: Sarif, report: String ->
32-
sarif.copy(runs = sarif.runs + report.jsonToSarif().runs)
33-
}.sarifToJson()
34-
35-
// internal
36-
37-
private fun String.jsonToSarif(): Sarif =
38-
jacksonObjectMapper().readValue(this)
39-
40-
private fun Sarif.sarifToJson(): String =
41-
jacksonObjectMapper()
42-
.setSerializationInclusion(JsonInclude.Include.NON_NULL)
43-
.writerWithDefaultPrettyPrinter()
44-
.writeValueAsString(this)
27+
sarif.copy(runs = sarif.runs + Sarif.fromJson(report).runs)
28+
}.toJson()
4529
}
4630

4731
/**
48-
* Creates a SARIF report and returns it as string
49-
*/
50-
fun createReport(): String =
51-
constructSarif().sarifToJson()
52-
53-
// internal
54-
55-
private val defaultLineNumber = 1 // if the line in the source code where the exception is thrown is unknown
56-
57-
/**
58-
* [Read more about links to locations](https://github.com/microsoft/sarif-tutorials/blob/main/docs/3-Beyond-basics.md#msg-links-location)
32+
* Creates a SARIF report.
5933
*/
60-
private val relatedLocationId = 1 // for attaching link to generated test in related locations
61-
62-
private fun constructSarif(): Sarif {
34+
fun createReport(): Sarif {
6335
val sarifResults = mutableListOf<SarifResult>()
6436
val sarifRules = mutableSetOf<SarifRule>()
6537

@@ -85,6 +57,15 @@ class SarifReport(
8557
)
8658
}
8759

60+
// internal
61+
62+
private val defaultLineNumber = 1 // if the line in the source code where the exception is thrown is unknown
63+
64+
/**
65+
* [Read more about links to locations](https://github.com/microsoft/sarif-tutorials/blob/main/docs/3-Beyond-basics.md#msg-links-location)
66+
*/
67+
private val relatedLocationId = 1 // for attaching link to generated test in related locations
68+
8869
/**
8970
* Minimizes detected errors and removes duplicates.
9071
*
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package org.utbot.intellij.plugin.inspection
2+
3+
import com.intellij.codeInspection.ex.*
4+
import com.intellij.openapi.project.Project
5+
import com.intellij.openapi.util.NotNullLazyValue
6+
import com.intellij.ui.content.ContentManager
7+
import org.utbot.sarif.Sarif
8+
import java.nio.file.Path
9+
10+
/**
11+
* Provides [InspectionProfileImpl] with only one inspection tool - [UTBotInspectionTool].
12+
*
13+
* @see GlobalInspectionContextImpl
14+
*/
15+
class UTBotInspectionContext(
16+
project: Project,
17+
contentManager: NotNullLazyValue<out ContentManager>,
18+
val sarifReports: MutableMap<Path, Sarif>
19+
) : GlobalInspectionContextImpl(project, contentManager) {
20+
21+
override fun getCurrentProfile(): InspectionProfileImpl {
22+
val utbotInspectionTool = UTBotInspectionTool.getInstance(sarifReports)
23+
val globalInspectionToolWrapper = GlobalInspectionToolWrapper(utbotInspectionTool)
24+
globalInspectionToolWrapper.initialize(this)
25+
val supplier = InspectionToolsSupplier.Simple(listOf(globalInspectionToolWrapper))
26+
return InspectionProfileImpl("UTBotInspectionToolProfile", supplier, BASE_PROFILE)
27+
}
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package org.utbot.intellij.plugin.inspection
2+
3+
import com.intellij.codeInspection.ex.GlobalInspectionContextImpl
4+
import com.intellij.codeInspection.ex.InspectionManagerEx
5+
import com.intellij.openapi.project.Project
6+
import com.intellij.openapi.util.NotNullLazyValue
7+
import com.intellij.ui.content.ContentManager
8+
import org.utbot.sarif.Sarif
9+
import java.nio.file.Path
10+
11+
/**
12+
* Creates [UTBotInspectionContext] with right arguments.
13+
*
14+
* Inheritance is needed to provide [UTBotInspectionContext] instead of [GlobalInspectionContextImpl].
15+
*
16+
* See [com.intellij.codeInspection.ex.InspectionManagerEx] for details.
17+
*/
18+
class UTBotInspectionManager(project: Project) : InspectionManagerEx(project) {
19+
20+
companion object {
21+
fun getInstance(project: Project, sarifReports: MutableMap<Path, Sarif>) =
22+
UTBotInspectionManager(project).also {
23+
it.sarifReports = sarifReports
24+
}
25+
}
26+
27+
private val myContentManager: NotNullLazyValue<ContentManager> by lazy {
28+
NotNullLazyValue.createValue {
29+
getProblemsViewContentManager(project)
30+
}
31+
}
32+
33+
private var sarifReports: MutableMap<Path, Sarif> = mutableMapOf()
34+
35+
override fun createNewGlobalContext(): GlobalInspectionContextImpl =
36+
UTBotInspectionContext(project, myContentManager, sarifReports)
37+
}

0 commit comments

Comments
 (0)