Skip to content

Commit df5bba9

Browse files
Another way to process failures in instrumented process not connected to concrete execution (#2270)
1 parent da3e553 commit df5bba9

File tree

6 files changed

+85
-28
lines changed

6 files changed

+85
-28
lines changed

utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/UtExecutionResult.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,14 @@ class InstrumentedProcessDeathException(cause: Throwable) :
7979

8080
data class UtConcreteExecutionFailure(override val exception: InstrumentedProcessDeathException) : UtExecutionFailure()
8181

82+
/**
83+
* Represents a failure in instrumented process
84+
* that is not actually caused by concrete execution.
85+
*
86+
* For example, failure may occured during data preparation for a concrete call.
87+
*/
88+
data class UtConcreteExecutionProcessedFailure(override val exception: Throwable): UtExecutionFailure()
89+
8290
val UtExecutionResult.isSuccess: Boolean
8391
get() = this is UtExecutionSuccess
8492

utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,13 @@ class UtBotSymbolicEngine(
280280
val concreteExecutionResult =
281281
concreteExecutor.executeConcretely(methodUnderTest, stateBefore, instrumentation, UtSettings.concreteExecutionDefaultTimeoutInInstrumentedProcessMillis)
282282

283+
concreteExecutionResult.processedFailure()?.let { failure ->
284+
emit(UtFailedExecution(stateBefore, failure))
285+
286+
logger.debug { "Instrumented process failed with exception ${failure.exception} before concrete execution started" }
287+
return@measureTime
288+
}
289+
283290
if (concreteExecutionResult.violatesUtMockAssumption()) {
284291
logger.debug { "Generated test case violates the UtMock assumption: $concreteExecutionResult" }
285292
return@measureTime
@@ -438,11 +445,18 @@ class UtBotSymbolicEngine(
438445
// in case an exception occurred from the concrete execution
439446
concreteExecutionResult ?: return@runJavaFuzzing BaseFeedback(result = Trie.emptyNode(), control = Control.PASS)
440447

448+
// in case of processed failure in the concrete execution
449+
concreteExecutionResult.processedFailure()?.let { failure ->
450+
logger.debug { "Instrumented process failed with exception ${failure.exception} before concrete execution started" }
451+
return@runJavaFuzzing BaseFeedback(result = Trie.emptyNode(), control = Control.PASS)
452+
}
453+
441454
if (concreteExecutionResult.violatesUtMockAssumption()) {
442455
logger.debug { "Generated test case by fuzzer violates the UtMock assumption: $concreteExecutionResult" }
443456
return@runJavaFuzzing BaseFeedback(result = Trie.emptyNode(), control = Control.PASS)
444457
}
445458

459+
val result = concreteExecutionResult.result
446460
val coveredInstructions = concreteExecutionResult.coverage.coveredInstructions
447461
var trieNode: Trie.Node<Instruction>? = null
448462
if (coveredInstructions.isNotEmpty()) {
@@ -455,7 +469,6 @@ class UtBotSymbolicEngine(
455469
}
456470
} else {
457471
logger.error { "Coverage is empty for $methodUnderTest with $values" }
458-
val result = concreteExecutionResult.result
459472
if (result is UtSandboxFailure) {
460473
val stackTraceElements = result.exception.stackTrace.reversed()
461474
if (errorStackTraceTracker.add(stackTraceElements).count > 1) {
@@ -594,6 +607,13 @@ class UtBotSymbolicEngine(
594607
UtSettings.concreteExecutionDefaultTimeoutInInstrumentedProcessMillis
595608
)
596609

610+
concreteExecutionResult.processedFailure()?.let { failure ->
611+
emit(UtFailedExecution(stateBefore, failure))
612+
613+
logger.debug { "Instrumented process failed with exception ${failure.exception} before concrete execution started" }
614+
return
615+
}
616+
597617
if (concreteExecutionResult.violatesUtMockAssumption()) {
598618
logger.debug { "Generated test case violates the UtMock assumption: $concreteExecutionResult" }
599619
return
@@ -749,5 +769,8 @@ private fun UtConcreteExecutionResult.violatesUtMockAssumption(): Boolean {
749769
return result.exceptionOrNull()?.javaClass?.name == UtMockAssumptionViolatedException::class.java.name
750770
}
751771

772+
private fun UtConcreteExecutionResult.processedFailure(): UtConcreteExecutionProcessedFailure?
773+
= result as? UtConcreteExecutionProcessedFailure
774+
752775
private fun checkStaticMethodsMock(execution: UtSymbolicExecution) =
753776
execution.instrumentation.any { it is UtStaticMethodInstrumentation}

utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/UtExecutionInstrumentation.kt

Lines changed: 12 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,12 @@ import org.utbot.instrumentation.instrumentation.execution.constructors.UtModelC
1212
import org.utbot.instrumentation.instrumentation.execution.mock.InstrumentationContext
1313
import org.utbot.instrumentation.instrumentation.execution.ndd.NonDeterministicClassVisitor
1414
import org.utbot.instrumentation.instrumentation.execution.ndd.NonDeterministicDetector
15+
import org.utbot.instrumentation.instrumentation.execution.phases.ConstructedData
1516
import org.utbot.instrumentation.instrumentation.execution.phases.PhasesController
16-
import org.utbot.instrumentation.instrumentation.execution.phases.start
1717
import org.utbot.instrumentation.instrumentation.instrumenter.Instrumenter
1818
import org.utbot.instrumentation.instrumentation.mock.MockClassVisitor
1919
import java.security.ProtectionDomain
2020
import kotlin.reflect.jvm.javaMethod
21-
import java.util.*
2221

2322
/**
2423
* Consists of the data needed to execute the method concretely. Also includes method arguments stored in models.
@@ -89,35 +88,22 @@ object UtExecutionInstrumentation : Instrumentation<UtConcreteExecutionResult> {
8988
}
9089
val (stateBefore, instrumentations, timeout) = parameters // smart cast to UtConcreteExecutionData
9190

92-
val methodId = clazz.singleExecutableId(methodSignature)
93-
val returnClassId = methodId.returnType
94-
9591
return PhasesController(
9692
instrumentationContext,
9793
traceHandler,
9894
delegateInstrumentation,
9995
timeout
10096
).computeConcreteExecutionResult {
10197
try {
102-
val (params, statics, cache) = this.executePhaseInTimeout(valueConstructionPhase) {
103-
val params = constructParameters(stateBefore)
104-
val statics = constructStatics(stateBefore)
105-
106-
// here static methods and instances are mocked
107-
mock(instrumentations)
108-
109-
Triple(params, statics, getCache())
98+
// some preparation actions for concrete execution
99+
var constructedData: ConstructedData
100+
try {
101+
constructedData = applyPreprocessing(parameters)
102+
} catch (t: Throwable) {
103+
return UtConcreteExecutionResult(MissingState, UtConcreteExecutionProcessedFailure(t), Coverage())
110104
}
111105

112-
// invariants:
113-
// 1. phase must always complete if started as static reset relies on it
114-
// 2. phase must be fast as there are no incremental changes
115-
postprocessingPhase.setStaticFields(preparationPhase.start {
116-
val result = setStaticFields(statics)
117-
resetTrace()
118-
resetND()
119-
result
120-
})
106+
val (params, statics, cache) = constructedData
121107

122108
// invocation
123109
val concreteResult = executePhaseInTimeout(invocationPhase) {
@@ -143,7 +129,8 @@ object UtExecutionInstrumentation : Instrumentation<UtConcreteExecutionResult> {
143129
val ndNews = constructNewInstrumentation(ndResults.news, ndResults.calls)
144130
val newInstrumentation = mergeInstrumentations(instrumentations, ndStatics, ndNews)
145131

146-
val executionResult = convertToExecutionResult(concreteResult, returnClassId)
132+
val returnType = clazz.singleExecutableId(methodSignature).returnType
133+
val executionResult = convertToExecutionResult(concreteResult,returnType)
147134

148135
val stateAfterParametersWithThis = constructParameters(params)
149136
val stateAfterStatics = constructStatics(stateBefore, statics)
@@ -164,10 +151,8 @@ object UtExecutionInstrumentation : Instrumentation<UtConcreteExecutionResult> {
164151
newInstrumentation
165152
))
166153
} finally {
167-
postprocessingPhase.start {
168-
resetStaticFields()
169-
valueConstructionPhase.resetMockMethods()
170-
}
154+
// restoring data after concrete execution
155+
applyPostprocessing()
171156
}
172157
}
173158
}

utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/phases/PhasesController.kt

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import org.utbot.framework.plugin.api.util.utContext
1111
import org.utbot.framework.plugin.api.util.withUtContext
1212
import org.utbot.instrumentation.instrumentation.Instrumentation
1313
import org.utbot.instrumentation.instrumentation.et.TraceHandler
14+
import org.utbot.instrumentation.instrumentation.execution.UtConcreteExecutionData
1415
import org.utbot.instrumentation.instrumentation.execution.UtConcreteExecutionResult
1516
import org.utbot.instrumentation.instrumentation.execution.mock.InstrumentationContext
1617
import java.security.AccessControlException
@@ -67,4 +68,36 @@ class PhasesController(
6768

6869
return@start result.getOrThrow() as T
6970
}
71+
72+
fun applyPreprocessing(parameters: UtConcreteExecutionData): ConstructedData {
73+
74+
val constructedData = executePhaseInTimeout(valueConstructionPhase) {
75+
val params = constructParameters(parameters.stateBefore)
76+
val statics = constructStatics(parameters.stateBefore)
77+
78+
// here static methods and instances are mocked
79+
mock(parameters.instrumentation)
80+
81+
ConstructedData(params, statics, getCache())
82+
}
83+
84+
// invariants:
85+
// 1. phase must always complete if started as static reset relies on it
86+
// 2. phase must be fast as there are no incremental changes
87+
postprocessingPhase.setStaticFields(preparationPhase.start {
88+
val result = setStaticFields(constructedData.statics)
89+
resetTrace()
90+
resetND()
91+
result
92+
})
93+
94+
return constructedData
95+
}
96+
97+
fun applyPostprocessing() {
98+
postprocessingPhase.start {
99+
resetStaticFields()
100+
valueConstructionPhase.resetMockMethods()
101+
}
102+
}
70103
}

utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/phases/ValueConstructionPhase.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ typealias ConstructedParameters = List<UtConcreteValue<*>>
1111
typealias ConstructedStatics = Map<FieldId, UtConcreteValue<*>>
1212
typealias ConstructedCache = IdentityHashMap<Any, UtModel>
1313

14+
data class ConstructedData(
15+
val params: ConstructedParameters,
16+
val statics: ConstructedStatics,
17+
val cache: ConstructedCache,
18+
)
19+
1420
/**
1521
* This phase of values instantiation from given models.
1622
*/

utbot-summary/src/main/kotlin/org/utbot/summary/TagGenerator.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package org.utbot.summary
22

33
import org.utbot.framework.plugin.api.Step
44
import org.utbot.framework.plugin.api.UtConcreteExecutionFailure
5+
import org.utbot.framework.plugin.api.UtConcreteExecutionProcessedFailure
56
import org.utbot.framework.plugin.api.UtExecution
67
import org.utbot.framework.plugin.api.UtExecutionResult
78
import org.utbot.framework.plugin.api.UtExecutionSuccess
@@ -220,6 +221,7 @@ private fun UtExecutionResult.clusterKind() = when (this) {
220221
is UtStreamConsumingFailure -> ExecutionGroup.ERROR_SUITE
221222
is UtOverflowFailure -> ExecutionGroup.OVERFLOWS
222223
is UtTimeoutException -> ExecutionGroup.TIMEOUTS
224+
is UtConcreteExecutionProcessedFailure -> ExecutionGroup.CRASH_SUITE
223225
is UtConcreteExecutionFailure -> ExecutionGroup.CRASH_SUITE
224226
is UtSandboxFailure -> ExecutionGroup.SECURITY
225227
is UtTaintAnalysisFailure -> ExecutionGroup.TAINT_ANALYSIS

0 commit comments

Comments
 (0)