Skip to content

Commit 731a2c6

Browse files
authored
Fix incomplete results in the SARIF report #470 (#471)
1 parent 07b2970 commit 731a2c6

File tree

3 files changed

+51
-22
lines changed

3 files changed

+51
-22
lines changed

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,8 +156,8 @@ data class SarifRegion(
156156
*/
157157
fun withStartLine(text: String, startLine: Int): SarifRegion {
158158
val neededLine = text.split('\n').getOrNull(startLine - 1) // to zero-based
159-
val startColumn = neededLine?.let {
160-
neededLine.takeWhile { it.toString().isBlank() }.length + 1 // to one-based
159+
val startColumn = neededLine?.run {
160+
takeWhile { it.toString().isBlank() }.length + 1 // to one-based
161161
}
162162
return SarifRegion(startLine = startLine, startColumn = startColumn)
163163
}

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

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -66,16 +66,13 @@ class SarifReport(
6666
*/
6767
private val relatedLocationId = 1 // for attaching link to generated test in related locations
6868

69-
private fun shouldProcessUncheckedException(result: UtExecutionResult) = (result is UtImplicitlyThrownException)
70-
|| ((result is UtOverflowFailure) && UtSettings.treatOverflowAsError)
71-
7269
private fun constructSarif(): Sarif {
7370
val sarifResults = mutableListOf<SarifResult>()
7471
val sarifRules = mutableSetOf<SarifRule>()
7572

7673
for (testCase in testCases) {
7774
for (execution in testCase.executions) {
78-
if (shouldProcessUncheckedException(execution.result)) {
75+
if (shouldProcessExecutionResult(execution.result)) {
7976
val (sarifResult, sarifRule) = processUncheckedException(
8077
method = testCase.method,
8178
utExecution = execution,
@@ -144,7 +141,7 @@ class SarifReport(
144141
if (classFqn == null)
145142
return listOf()
146143
val sourceRelativePath = sourceFinding.getSourceRelativePath(classFqn)
147-
val startLine = extractLineNumber(utExecution) ?: defaultLineNumber
144+
val startLine = getLastLineNumber(utExecution) ?: defaultLineNumber
148145
val sourceCode = sourceFinding.getSourceFile(classFqn)?.readText() ?: ""
149146
val sourceRegion = SarifRegion.withStartLine(sourceCode, startLine)
150147
return listOf(
@@ -301,10 +298,24 @@ class SarifReport(
301298
return "..."
302299
}
303300

304-
private fun extractLineNumber(utExecution: UtExecution): Int? =
305-
try {
301+
/**
302+
* Returns the number of the last line in the execution path.
303+
*/
304+
private fun getLastLineNumber(utExecution: UtExecution): Int? {
305+
val lastPathLine = try {
306306
utExecution.path.lastOrNull()?.stmt?.javaSourceStartLineNumber
307307
} catch (t: Throwable) {
308308
null
309309
}
310+
// if for some reason we can't extract the last line from the path
311+
val lastCoveredInstruction =
312+
utExecution.coverage?.coveredInstructions?.lastOrNull()?.lineNumber
313+
return lastPathLine ?: lastCoveredInstruction
314+
}
315+
316+
private fun shouldProcessExecutionResult(result: UtExecutionResult): Boolean {
317+
val implicitlyThrown = result is UtImplicitlyThrownException
318+
val overflowFailure = result is UtOverflowFailure && UtSettings.treatOverflowAsError
319+
return implicitlyThrown || overflowFailure
320+
}
310321
}
Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,43 @@
11
package org.utbot.intellij.plugin.sarif
22

3+
import com.intellij.psi.JavaPsiFacade
4+
import com.intellij.psi.PsiClass
5+
import org.jetbrains.kotlin.idea.search.allScope
36
import org.utbot.common.PathUtil.classFqnToPath
47
import org.utbot.common.PathUtil.safeRelativize
58
import org.utbot.common.PathUtil.toPath
69
import org.utbot.sarif.SourceFindingStrategy
7-
import com.intellij.psi.JavaPsiFacade
8-
import com.intellij.psi.PsiClass
9-
import org.jetbrains.kotlin.idea.search.allScope
1010
import java.io.File
1111

1212
/**
13-
* The search strategy based on the information available to the PsiClass
13+
* The search strategy based on the information available to the PsiClass.
1414
*/
1515
class SourceFindingStrategyIdea(testClass: PsiClass) : SourceFindingStrategy() {
1616

1717
/**
18-
* Returns the relative path (against `project.basePath`) to the file with generated tests
18+
* Returns the relative path (against `project.basePath`) to the file with generated tests.
1919
*/
2020
override val testsRelativePath: String
2121
get() = safeRelativize(project.basePath, testsFilePath)
2222
?: testsFilePath.toPath().fileName.toString()
2323

2424
/**
25-
* Returns the relative path (against `project.basePath`) to the source file containing the class [classFqn]
25+
* Returns the relative path (against `project.basePath`) to the source file containing the class [classFqn].
2626
*/
27-
override fun getSourceRelativePath(classFqn: String, extension: String?): String =
28-
JavaPsiFacade.getInstance(project)
29-
.findClass(classFqn, project.allScope())?.let { psiClass ->
30-
safeRelativize(project.basePath, psiClass.containingFile.virtualFile.path)
31-
} ?: (classFqnToPath(classFqn) + (extension ?: defaultExtension))
27+
override fun getSourceRelativePath(classFqn: String, extension: String?): String {
28+
val psiClass = findPsiClass(classFqn)
29+
val absolutePath = psiClass?.containingFile?.virtualFile?.path
30+
val relativePath = safeRelativize(project.basePath, absolutePath)
31+
val defaultRelativePath = classFqnToPath(classFqn) + (extension ?: defaultExtension)
32+
return relativePath ?: defaultRelativePath
33+
}
3234

3335
/**
3436
* Finds the source file containing the class [classFqn].
3537
* Returns null if the file does not exist.
3638
*/
3739
override fun getSourceFile(classFqn: String, extension: String?): File? {
38-
val psiClass = JavaPsiFacade.getInstance(project).findClass(classFqn, project.allScope())
40+
val psiClass = findPsiClass(classFqn)
3941
val sourceCodeFile = psiClass?.containingFile?.virtualFile?.path?.let(::File)
4042
return if (sourceCodeFile?.exists() == true) sourceCodeFile else null
4143
}
@@ -48,7 +50,23 @@ class SourceFindingStrategyIdea(testClass: PsiClass) : SourceFindingStrategy() {
4850

4951
/**
5052
* The file extension to be used in [getSourceRelativePath] if the source file
51-
* was not found by the class qualified name and the `extension` parameter is null
53+
* was not found by the class qualified name and the `extension` parameter is null.
5254
*/
5355
private val defaultExtension = "." + (testClass.containingFile.virtualFile.extension ?: "java")
56+
57+
/**
58+
* Returns PsiClass by given [classFqn].
59+
*/
60+
private fun findPsiClass(classFqn: String): PsiClass? {
61+
val psiFacade = JavaPsiFacade.getInstance(project)
62+
val psiClass = psiFacade.findClass(classFqn, project.allScope())
63+
if (psiClass != null)
64+
return psiClass
65+
66+
// If for some reason `psiClass` was not found by the `findClass` method
67+
val packageName = classFqn.substringBeforeLast('.')
68+
val shortClassName = classFqn.substringAfterLast('.')
69+
val neededPackage = psiFacade.findPackage(packageName)
70+
return neededPackage?.classes?.firstOrNull { it.name == shortClassName }
71+
}
5472
}

0 commit comments

Comments
 (0)