Skip to content

Commit

Permalink
Add timeout tests to SARIF report (#1496)
Browse files Browse the repository at this point in the history
* Add timeout tests to SARIF report
* Add unit tests
  • Loading branch information
mmvpm authored Dec 9, 2022
1 parent 7ba93d6 commit 1042fb7
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,49 @@ class SarifReportTest {
assert(result.message.text.contains("AccessControlException"))
}

@Test
fun testProcessTimeoutException() {
mockUtMethodNames()

val timeoutMessage = "Timeout 1000 elapsed"
val timeoutException = TimeoutException(timeoutMessage)
Mockito.`when`(mockUtExecution.result).thenReturn(UtTimeoutException(timeoutException))

defaultMockCoverage(mockUtExecution)

val report = sarifReportMain.createReport()
val result = report.runs.first().results.first()
assert(result.message.text.contains(timeoutMessage))
}

@Test
fun testConvertCoverageToStackTrace() {
mockUtMethodNames()

val timeoutException = TimeoutException("Timeout 1000 elapsed")
Mockito.`when`(mockUtExecution.result).thenReturn(UtTimeoutException(timeoutException))

val classMainPath = "com/abc/Main"
val classUtilPath = "com/cba/Util"
Mockito.`when`(mockUtExecution.coverage?.coveredInstructions).thenReturn(listOf(
Instruction(classMainPath, "main(l)l", 3, 1),
Instruction(classMainPath, "main(l)l", 4, 2),
Instruction(classMainPath, "main(l)l", 4, 3), // last for main
Instruction(classUtilPath, "util(ll)l", 6, 4),
Instruction(classUtilPath, "util(ll)l", 7, 5), // last for util
))

val report = sarifReportMain.createReport()
val result = report.runs.first().results.first()
val stackTrace = result.codeFlows.first().threadFlows.first().locations

assert(stackTrace[0].location.physicalLocation.artifactLocation.uri == "$classMainPath.java")
assert(stackTrace[0].location.physicalLocation.region.startLine == 4)

assert(stackTrace[1].location.physicalLocation.artifactLocation.uri == "$classUtilPath.java")
assert(stackTrace[1].location.physicalLocation.region.startLine == 7)
}

@Test
fun testCodeFlowsStartsWithMethodCall() {
mockUtMethodNames()
Expand Down
53 changes: 49 additions & 4 deletions utbot-framework/src/main/kotlin/org/utbot/sarif/SarifReport.kt
Original file line number Diff line number Diff line change
Expand Up @@ -153,12 +153,17 @@ class SarifReport(
val methodArguments = utExecution.stateBefore.parameters
.joinToString(prefix = "", separator = ", ", postfix = "") { it.preview() }

val errorMessage = if (executionFailure is UtTimeoutException)
"Unexpected behavior: ${executionFailure.exception.message}"
else
"Unexpected exception: ${executionFailure.exception}"

val sarifResult = SarifResult(
ruleId,
Level.Error,
Message(
text = """
Unexpected exception: ${executionFailure.exception}.
$errorMessage.
Test case: `$methodName($methodArguments)`
[Generated test for this case]($relatedLocationId)
""".trimIndent()
Expand Down Expand Up @@ -229,8 +234,10 @@ class SarifReport(
val stackTraceResolved = filterStackTrace(method, utExecution, executionFailure)
.mapNotNull { findStackTraceElementLocation(it) }
.toMutableList()
if (stackTraceResolved.isEmpty())
return listOf() // empty stack trace is not shown

if (stackTraceResolved.isEmpty()) {
stackTraceResolved += stackTraceFromCoverage(utExecution) // fallback logic
}

// prepending stack trace by `method` call in generated tests
val methodCallLocation: SarifPhysicalLocation? =
Expand Down Expand Up @@ -279,6 +286,10 @@ class SarifReport(
if (lastMethodCallIndex != -1) {
// taking all elements before the last `method` call
stackTrace = stackTrace.take(lastMethodCallIndex + 1)
} else { // no `method` call in the stack trace
if (executionFailure.exception !is StackOverflowError) {
stackTrace = listOf() // (likely) the stack trace contains only our internal calls
}
}

if (executionFailure.exception is StackOverflowError) {
Expand All @@ -292,6 +303,39 @@ class SarifReport(
return stackTraceFiltered
}

/**
* Constructs the stack trace from the list of covered instructions.
*/
private fun stackTraceFromCoverage(utExecution: UtExecution): List<SarifFlowLocationWrapper> {
val coveredInstructions = utExecution.coverage?.coveredInstructions
?: return listOf()

val executionTrace = coveredInstructions.groupBy { instruction ->
instruction.className to instruction.methodSignature // group by method
}.map { (_, instructionsForOneMethod) ->
instructionsForOneMethod.last() // we need only last to construct the stack trace
}

val sarifExecutionTrace = executionTrace.map { instruction ->
val classFqn = instruction.className.replace('/', '.')
val methodName = instruction.methodSignature.substringBefore('(')
val lineNumber = instruction.lineNumber

val sourceFilePath = sourceFinding.getSourceRelativePath(classFqn)
val sourceFileName = sourceFilePath.substringAfterLast('/')

SarifFlowLocationWrapper(SarifFlowLocation(
message = Message("$classFqn.$methodName($sourceFileName:$lineNumber)"),
physicalLocation = SarifPhysicalLocation(
SarifArtifact(uri = sourceFilePath),
SarifRegion(startLine = lineNumber)
)
))
}

return sarifExecutionTrace.reversed() // to stack trace
}

private fun findStackTraceElementLocation(stackTraceElement: StackTraceElement): SarifFlowLocationWrapper? {
val lineNumber = stackTraceElement.lineNumber
if (lineNumber < 1)
Expand Down Expand Up @@ -429,6 +473,7 @@ class SarifReport(
val overflowFailure = result is UtOverflowFailure && UtSettings.treatOverflowAsError
val assertionError = result is UtExplicitlyThrownException && result.exception is AssertionError
val sandboxFailure = result is UtSandboxFailure
return implicitlyThrown || overflowFailure || assertionError || sandboxFailure
val timeoutException = result is UtTimeoutException
return implicitlyThrown || overflowFailure || assertionError || sandboxFailure || timeoutException
}
}

0 comments on commit 1042fb7

Please sign in to comment.