Skip to content

Commit 6488a07

Browse files
Improve exception rendering in parametrized tests №641 (#690)
1 parent 2746482 commit 6488a07

File tree

7 files changed

+77
-25
lines changed

7 files changed

+77
-25
lines changed

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/context/CgContext.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,10 @@ internal interface CgContextOwner {
171171
// a variable representing an actual result of the method under test call
172172
var actual: CgVariable
173173

174+
// a variable representing if test method contains reflective call or not
175+
// and should we catch exceptions like InvocationTargetException or not so on
176+
var containsReflectiveCall: Boolean
177+
174178
// map from a set of tests for a method to another map
175179
// which connects code generation error message
176180
// with the number of times it occurred
@@ -420,7 +424,8 @@ internal data class CgContext(
420424
override val runtimeExceptionTestsBehaviour: RuntimeExceptionTestsBehaviour =
421425
RuntimeExceptionTestsBehaviour.defaultItem,
422426
override val hangingTestsTimeout: HangingTestsTimeout = HangingTestsTimeout(),
423-
override val enableTestsTimeout: Boolean = true
427+
override val enableTestsTimeout: Boolean = true,
428+
override var containsReflectiveCall: Boolean = false,
424429
) : CgContextOwner {
425430
override lateinit var statesCache: EnvironmentFieldStateCache
426431
override lateinit var actual: CgVariable

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgCallableAccessManager.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,7 @@ internal class CgCallableAccessManagerImpl(val context: CgContext) : CgCallableA
359359
}
360360

361361
private fun MethodId.callWithReflection(caller: CgExpression?, args: List<CgExpression>): CgMethodCall {
362+
containsReflectiveCall = true
362363
val method = declaredExecutableRefs[this]
363364
?: toExecutableVariable(args).also {
364365
declaredExecutableRefs = declaredExecutableRefs.put(this, it)
@@ -372,6 +373,7 @@ internal class CgCallableAccessManagerImpl(val context: CgContext) : CgCallableA
372373
}
373374

374375
private fun ConstructorId.callWithReflection(args: List<CgExpression>): CgExecutableCall {
376+
containsReflectiveCall = true
375377
val constructor = declaredExecutableRefs[this]
376378
?: this.toExecutableVariable(args).also {
377379
declaredExecutableRefs = declaredExecutableRefs.put(this, it)

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import org.utbot.framework.codegen.model.tree.CgExecutableCall
4141
import org.utbot.framework.codegen.model.tree.CgExpression
4242
import org.utbot.framework.codegen.model.tree.CgFieldAccess
4343
import org.utbot.framework.codegen.model.tree.CgGetJavaClass
44+
import org.utbot.framework.codegen.model.tree.CgIsInstance
4445
import org.utbot.framework.codegen.model.tree.CgLiteral
4546
import org.utbot.framework.codegen.model.tree.CgMethod
4647
import org.utbot.framework.codegen.model.tree.CgMethodCall
@@ -1195,20 +1196,6 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c
11951196
val testName = nameGenerator.parameterizedTestMethodName(dataProviderMethodName)
11961197
withNameScope {
11971198
val testParameterDeclarations = createParameterDeclarations(testSet, genericExecution)
1198-
val mainBody = {
1199-
substituteStaticFields(statics, isParametrized = true)
1200-
// build this instance
1201-
thisInstance = genericExecution.stateBefore.thisInstance?.let { currentMethodParameters[CgParameterKind.ThisInstance] }
1202-
1203-
// build arguments for method under test and parameterized test
1204-
for (index in genericExecution.stateBefore.parameters.indices) {
1205-
methodArguments += currentMethodParameters[CgParameterKind.Argument(index)]!!
1206-
}
1207-
1208-
//record result and generate result assertions
1209-
recordActualResult()
1210-
generateAssertionsForParameterizedTest()
1211-
}
12121199

12131200
methodType = PARAMETRIZED
12141201
testMethod(
@@ -1219,21 +1206,39 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c
12191206
dataProviderMethodName
12201207
) {
12211208
rememberInitialStaticFields(statics)
1209+
substituteStaticFields(statics, isParametrized = true)
1210+
1211+
// build this instance
1212+
thisInstance = genericExecution.stateBefore.thisInstance?.let { currentMethodParameters[CgParameterKind.ThisInstance] }
1213+
1214+
// build arguments for method under test and parameterized test
1215+
for (index in genericExecution.stateBefore.parameters.indices) {
1216+
methodArguments += currentMethodParameters[CgParameterKind.Argument(index)]!!
1217+
}
12221218

12231219
if (containsFailureExecution(testSet) || statics.isNotEmpty()) {
12241220
var currentTryBlock = tryBlock {
1225-
mainBody()
1221+
recordActualResult()
1222+
generateAssertionsForParameterizedTest()
12261223
}
12271224

12281225
if (containsFailureExecution(testSet)) {
1229-
currentTryBlock = currentTryBlock.catch(Throwable::class.java.id) { e ->
1230-
val pseudoExceptionVarName = when (codegenLanguage) {
1231-
CodegenLanguage.JAVA -> "${expectedErrorVarName}.isInstance(${e.name.decapitalize()})"
1232-
CodegenLanguage.KOTLIN -> "${expectedErrorVarName}!!.isInstance(${e.name.decapitalize()})"
1226+
val expectedErrorVariable = currentMethodParameters[CgParameterKind.ExpectedException]
1227+
?: error("Test set $testSet contains failure execution, but test method signature has no error parameter")
1228+
currentTryBlock =
1229+
if (containsReflectiveCall) {
1230+
currentTryBlock.catch(InvocationTargetException::class.java.id) { exception ->
1231+
testFrameworkManager.assertBoolean(
1232+
expectedErrorVariable.isInstance(exception[getTargetException]())
1233+
)
1234+
}
1235+
} else {
1236+
currentTryBlock.catch(Throwable::class.java.id) { throwable ->
1237+
testFrameworkManager.assertBoolean(
1238+
expectedErrorVariable.isInstance(throwable)
1239+
)
1240+
}
12331241
}
1234-
1235-
testFrameworkManager.assertBoolean(CgVariable(pseudoExceptionVarName, booleanClassId))
1236-
}
12371242
}
12381243

12391244
if (statics.isNotEmpty()) {
@@ -1243,7 +1248,8 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c
12431248
}
12441249
+currentTryBlock
12451250
} else {
1246-
mainBody()
1251+
recordActualResult()
1252+
generateAssertionsForParameterizedTest()
12471253
}
12481254
}
12491255
}
@@ -1341,7 +1347,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c
13411347
name = nameGenerator.variableName(expectedErrorVarName)
13421348
),
13431349
// exceptions are always reference type
1344-
isReferenceType = true
1350+
isReferenceType = true,
13451351
)
13461352
this += expectedException
13471353
currentMethodParameters[CgParameterKind.ExpectedException] = expectedException.parameter
@@ -1466,6 +1472,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c
14661472
thisInstance = null
14671473
methodArguments.clear()
14681474
currentExecution = null
1475+
containsReflectiveCall = false
14691476
mockFrameworkManager.clearExecutionResources()
14701477
currentMethodParameters.clear()
14711478
}

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/util/CgStatementConstructor.kt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ import org.utbot.framework.plugin.api.util.objectArrayClassId
5858
import org.utbot.framework.plugin.api.util.objectClassId
5959
import fj.data.Either
6060
import org.utbot.framework.codegen.model.tree.CgArrayInitializer
61+
import org.utbot.framework.codegen.model.tree.CgIsInstance
62+
import org.utbot.framework.plugin.api.util.classClassId
6163
import java.lang.reflect.Constructor
6264
import java.lang.reflect.Method
6365
import kotlin.reflect.KFunction
@@ -116,6 +118,8 @@ interface CgStatementConstructor {
116118
fun CgTryCatch.catch(exception: ClassId, init: (CgVariable) -> Unit): CgTryCatch
117119
fun CgTryCatch.finally(init: () -> Unit): CgTryCatch
118120

121+
fun CgExpression.isInstance(value: CgExpression): CgIsInstance
122+
119123
fun innerBlock(init: () -> Unit): CgInnerBlock
120124

121125
// fun CgTryCatchBuilder.statements(init: () -> Unit)
@@ -302,6 +306,15 @@ internal class CgStatementConstructorImpl(context: CgContext) :
302306
return this.copy(finally = finallyBlock)
303307
}
304308

309+
override fun CgExpression.isInstance(value: CgExpression): CgIsInstance {
310+
require(this.type == classClassId) {
311+
"isInstance method can be called on object with type $classClassId only, but actual type is ${this.type}"
312+
}
313+
314+
//TODO: we should better process it as this[isInstanceMethodId](value) as it is a call
315+
return CgIsInstance(this, value)
316+
}
317+
305318
override fun innerBlock(init: () -> Unit): CgInnerBlock =
306319
CgInnerBlock(block(init)).also {
307320
currentBlock += it

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/tree/CgElement.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ interface CgElement {
7575
is CgDeclaration -> visit(element)
7676
is CgAssignment -> visit(element)
7777
is CgTypeCast -> visit(element)
78+
is CgIsInstance -> visit(element)
7879
is CgThisInstance -> visit(element)
7980
is CgNotNullAssertion -> visit(element)
8081
is CgVariable -> visit(element)
@@ -537,6 +538,16 @@ class CgTypeCast(
537538
override val type: ClassId = targetType
538539
}
539540

541+
/**
542+
* Represents [java.lang.Class.isInstance] method.
543+
*/
544+
class CgIsInstance(
545+
val classExpression: CgExpression,
546+
val value: CgExpression,
547+
): CgExpression {
548+
override val type: ClassId = booleanClassId
549+
}
550+
540551
// Value
541552

542553
// TODO in general CgLiteral is not CgReferenceExpression because it can hold primitive values

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/CgAbstractRenderer.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import org.utbot.framework.codegen.model.tree.CgGreaterThan
3939
import org.utbot.framework.codegen.model.tree.CgIfStatement
4040
import org.utbot.framework.codegen.model.tree.CgIncrement
4141
import org.utbot.framework.codegen.model.tree.CgInnerBlock
42+
import org.utbot.framework.codegen.model.tree.CgIsInstance
4243
import org.utbot.framework.codegen.model.tree.CgLessThan
4344
import org.utbot.framework.codegen.model.tree.CgLiteral
4445
import org.utbot.framework.codegen.model.tree.CgLogicalAnd
@@ -422,6 +423,15 @@ internal abstract class CgAbstractRenderer(val context: CgContext, val printer:
422423
print("${element.variable.name}--")
423424
}
424425

426+
// isInstance check
427+
428+
override fun visit(element: CgIsInstance) {
429+
element.classExpression.accept(this)
430+
print(".isInstance(")
431+
element.value.accept(this)
432+
print(")")
433+
}
434+
425435
// Try-catch
426436

427437
override fun visit(element: CgTryCatch) {

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/CgVisitor.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import org.utbot.framework.codegen.model.tree.CgGreaterThan
4242
import org.utbot.framework.codegen.model.tree.CgIfStatement
4343
import org.utbot.framework.codegen.model.tree.CgIncrement
4444
import org.utbot.framework.codegen.model.tree.CgInnerBlock
45+
import org.utbot.framework.codegen.model.tree.CgIsInstance
4546
import org.utbot.framework.codegen.model.tree.CgLessThan
4647
import org.utbot.framework.codegen.model.tree.CgLiteral
4748
import org.utbot.framework.codegen.model.tree.CgLogicalAnd
@@ -180,6 +181,9 @@ interface CgVisitor<R> {
180181
// Type cast
181182
fun visit(element: CgTypeCast): R
182183

184+
// isInstance check
185+
fun visit(element: CgIsInstance): R
186+
183187
// This instance
184188
fun visit(element: CgThisInstance): R
185189

0 commit comments

Comments
 (0)