Skip to content

Commit 86ddf21

Browse files
committed
Fix ClassWithEnumTest.testOrdinal test
The problem was in the parameterized tests. There were multiple bugs: wrong class ids (class id's name must not include generic parameters, but they did), wrong method id for Arguments.arguments() method (its argument must be vararg, but it wasn't). Also, when a method accepts a vararg and is called via reflection, an array of vararg arguments should be additionally wrapped into an array of Objects (e.g. new Object[]{new Integer[]{1, 2, 3}}). However, when calling this method without reflection there must not be any such wrappers.
1 parent 954ea04 commit 86ddf21

File tree

10 files changed

+184
-104
lines changed

10 files changed

+184
-104
lines changed

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.utbot.framework.plugin.api.util
22

3+
import org.utbot.framework.plugin.api.ClassId
34
import org.utbot.framework.plugin.api.ExecutableId
45
import java.lang.reflect.Constructor
56
import java.lang.reflect.Executable
@@ -39,6 +40,23 @@ fun Constructor<*>.bytecodeSignature() = buildString {
3940

4041
fun Class<*>.bytecodeSignature(): String = id.jvmName
4142

43+
/**
44+
* Method [Class.getName] works differently for arrays than for other types.
45+
* - When an element of an array is a reference type (e.g. `java.lang.Object`),
46+
* the array of `java.lang.Object` will have name `[Ljava.lang.Object;`.
47+
* - When an element of an array is a primitive type (e.g. `int`),
48+
* the array of `int` will have name `[I`.
49+
*
50+
* So, this property returns the name of the given class in the format of an array element type name.
51+
* Basically, it performs conversions for primitives and reference types (e.g. `int` -> `I`, `java.lang.Object` -> `Ljava.lang.Object;`.
52+
*/
53+
val ClassId.arrayLikeName: String
54+
get() = when {
55+
isPrimitive -> primitiveTypeJvmNameOrNull()!!
56+
isRefType -> "L$name;"
57+
else -> name
58+
}
59+
4260
fun String.toReferenceTypeBytecodeSignature(): String {
4361
val packageName = this
4462
.takeIf { "." in this }

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

Lines changed: 56 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import org.utbot.common.packageName
55
import org.utbot.engine.isStatic
66
import org.utbot.framework.assemble.assemble
77
import org.utbot.framework.codegen.ForceStaticMocking
8-
import org.utbot.framework.codegen.JUNIT5_PARAMETERIZED_PACKAGE
98
import org.utbot.framework.codegen.Junit4
109
import org.utbot.framework.codegen.Junit5
1110
import org.utbot.framework.codegen.ParametrizedTestSource
@@ -26,6 +25,7 @@ import org.utbot.framework.codegen.model.constructor.util.EnvironmentFieldStateC
2625
import org.utbot.framework.codegen.model.constructor.util.FieldStateCache
2726
import org.utbot.framework.codegen.model.constructor.util.classCgClassId
2827
import org.utbot.framework.codegen.model.constructor.util.needExpectedDeclaration
28+
import org.utbot.framework.codegen.model.constructor.util.newArrayOf
2929
import org.utbot.framework.codegen.model.constructor.util.overridesEquals
3030
import org.utbot.framework.codegen.model.constructor.util.plus
3131
import org.utbot.framework.codegen.model.constructor.util.typeCast
@@ -67,7 +67,6 @@ import org.utbot.framework.codegen.model.tree.CgTryCatch
6767
import org.utbot.framework.codegen.model.tree.CgTypeCast
6868
import org.utbot.framework.codegen.model.tree.CgValue
6969
import org.utbot.framework.codegen.model.tree.CgVariable
70-
import org.utbot.framework.codegen.model.tree.buildForLoop
7170
import org.utbot.framework.codegen.model.tree.buildParameterizedTestDataProviderMethod
7271
import org.utbot.framework.codegen.model.tree.buildTestMethod
7372
import org.utbot.framework.codegen.model.tree.convertDocToCg
@@ -121,6 +120,7 @@ import org.utbot.framework.plugin.api.UtVoidModel
121120
import org.utbot.framework.plugin.api.onFailure
122121
import org.utbot.framework.plugin.api.onSuccess
123122
import org.utbot.framework.plugin.api.util.booleanClassId
123+
import org.utbot.framework.plugin.api.util.builtinMethodId
124124
import org.utbot.framework.plugin.api.util.doubleArrayClassId
125125
import org.utbot.framework.plugin.api.util.doubleClassId
126126
import org.utbot.framework.plugin.api.util.doubleWrapperClassId
@@ -1285,7 +1285,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c
12851285
if (containsFailureExecution) {
12861286
val expectedException = CgParameterDeclaration(
12871287
parameter = declareParameter(
1288-
type = throwableClassId(),
1288+
type = Class::class.id,
12891289
name = nameGenerator.variableName(expectedErrorVarName)
12901290
),
12911291
// exceptions are always reference type
@@ -1422,7 +1422,13 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c
14221422
arguments: List<CgExpression>,
14231423
) {
14241424
when (testFramework) {
1425-
Junit5 -> argsVariable[addToListMethodId](argumentsClassId[argumentsMethodId](arguments))
1425+
Junit5 -> {
1426+
+argsVariable[addToListMethodId](
1427+
argumentsClassId[argumentsMethodId](
1428+
newArrayOf(objectClassId, arguments)
1429+
)
1430+
)
1431+
}
14261432
TestNg -> {
14271433
val argsArray = newVar(objectArrayClassId, "testCaseObjects") {
14281434
CgAllocateArray(Array<Any?>::class.java.id, objectClassId, arguments.size)
@@ -1442,7 +1448,9 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c
14421448
*/
14431449
private fun setArgumentsArrayElement(array: CgVariable, index: Int, value: CgExpression) {
14441450
when (array.type) {
1445-
objectClassId -> java.lang.reflect.Array::class.id[setArrayElement](array, index, value)
1451+
objectClassId -> {
1452+
+java.lang.reflect.Array::class.id[setArrayElement](array, index, value)
1453+
}
14461454
else -> array.at(index) `=` value
14471455
}
14481456
}
@@ -1470,13 +1478,13 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c
14701478
val argListName = "argList"
14711479
return when (testFramework) {
14721480
Junit5 ->
1473-
newVar(argListClassId(), argListName) {
1474-
val constructor = ConstructorId(argListClassId(), emptyList())
1481+
newVar(argListClassId, argListName) {
1482+
val constructor = ConstructorId(argListClassId, emptyList())
14751483
constructor.invoke()
14761484
}
14771485
TestNg ->
1478-
newVar(argListClassId(), argListName) {
1479-
CgAllocateArray(argListClassId(), Array<Any>::class.java.id, length)
1486+
newVar(argListClassId, argListName) {
1487+
CgAllocateArray(argListClassId, Array<Any>::class.java.id, length)
14801488
}
14811489
Junit4 -> error("Parameterized tests are not supported for JUnit4")
14821490
}
@@ -1485,69 +1493,59 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c
14851493
/**
14861494
* Creates a [ClassId] for arguments collection.
14871495
*/
1488-
private fun argListClassId(): ClassId = when (testFramework) {
1489-
Junit5 -> BuiltinClassId(
1490-
name = "java.util.ArrayList<${JUNIT5_PARAMETERIZED_PACKAGE}.provider.Arguments>",
1491-
simpleName = "ArrayList<${JUNIT5_PARAMETERIZED_PACKAGE}.provider.Arguments>",
1492-
canonicalName = "java.util.ArrayList<${JUNIT5_PARAMETERIZED_PACKAGE}.provider.Arguments>",
1493-
packageName = "java.util",
1494-
)
1495-
TestNg -> BuiltinClassId(
1496-
name = Array<Array<Any?>?>::class.java.name,
1497-
simpleName = when (codegenLanguage) {
1498-
CodegenLanguage.JAVA -> "Object[][]"
1499-
CodegenLanguage.KOTLIN -> "Array<Array<Any?>?>"
1500-
},
1501-
canonicalName = Array<Array<Any?>?>::class.java.canonicalName,
1502-
packageName = Array<Array<Any?>?>::class.java.packageName,
1503-
)
1504-
Junit4 -> error("Parameterized tests are not supported for JUnit4")
1505-
}
1496+
private val argListClassId: ClassId
1497+
get() = when (testFramework) {
1498+
Junit5 -> java.util.ArrayList::class.id
1499+
TestNg -> BuiltinClassId(
1500+
name = Array<Array<Any?>?>::class.java.name,
1501+
simpleName = when (codegenLanguage) {
1502+
CodegenLanguage.JAVA -> "Object[][]"
1503+
CodegenLanguage.KOTLIN -> "Array<Array<Any?>?>"
1504+
},
1505+
canonicalName = Array<Array<Any?>?>::class.java.canonicalName,
1506+
packageName = Array<Array<Any?>?>::class.java.packageName,
1507+
)
1508+
Junit4 -> error("Parameterized tests are not supported for JUnit4")
1509+
}
15061510

15071511

15081512
/**
15091513
* A [MethodId] to add an item into [ArrayList].
15101514
*/
1511-
private val addToListMethodId: MethodId = methodId(
1512-
classId = ArrayList::class.id,
1513-
name = "add",
1514-
returnType = booleanClassId,
1515-
arguments = arrayOf(Object::class.id),
1516-
)
1515+
private val addToListMethodId: MethodId
1516+
get() = methodId(
1517+
classId = ArrayList::class.id,
1518+
name = "add",
1519+
returnType = booleanClassId,
1520+
arguments = arrayOf(Object::class.id),
1521+
)
15171522

15181523
/**
15191524
* A [ClassId] of class `org.junit.jupiter.params.provider.Arguments`
15201525
*/
1521-
private val argumentsClassId: ClassId = BuiltinClassId(
1522-
name = "org.junit.jupiter.params.provider.Arguments",
1523-
simpleName = "Arguments",
1524-
canonicalName = "org.junit.jupiter.params.provider.Arguments",
1525-
packageName = "org.junit.jupiter.params.provider",
1526-
)
1526+
private val argumentsClassId: BuiltinClassId
1527+
get() = BuiltinClassId(
1528+
name = "org.junit.jupiter.params.provider.Arguments",
1529+
simpleName = "Arguments",
1530+
canonicalName = "org.junit.jupiter.params.provider.Arguments",
1531+
packageName = "org.junit.jupiter.params.provider"
1532+
)
15271533

15281534
/**
15291535
* A [MethodId] to call JUnit Arguments method.
15301536
*/
1531-
private val argumentsMethodId: MethodId = methodId(
1532-
classId = argumentsClassId,
1533-
name = "arguments",
1534-
returnType = argumentsClassId,
1535-
arguments = arrayOf(Object::class.id),
1536-
)
1537+
private val argumentsMethodId: BuiltinMethodId
1538+
get() = builtinMethodId(
1539+
classId = argumentsClassId,
1540+
name = "arguments",
1541+
returnType = argumentsClassId,
1542+
// vararg of Objects
1543+
arguments = arrayOf(objectArrayClassId)
1544+
)
15371545

15381546
private fun containsFailureExecution(testSet: UtMethodTestSet) =
15391547
testSet.executions.any { it.result is UtExecutionFailure }
15401548

1541-
/**
1542-
* A [ClassId] for Class<Throwable>.
1543-
*/
1544-
private fun throwableClassId(): ClassId = BuiltinClassId(
1545-
name = "java.lang.Class<Throwable>",
1546-
simpleName = "Class<Throwable>",
1547-
canonicalName = "java.lang.Class<Throwable>",
1548-
packageName = "java.lang",
1549-
)
1550-
15511549

15521550
private fun collectParameterizedTestAnnotations(dataProviderMethodName: String?): Set<CgAnnotation> =
15531551
when (testFramework) {
@@ -1637,12 +1635,12 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c
16371635
private fun dataProviderMethod(dataProviderMethodName: String, body: () -> Unit): CgParameterizedTestDataProviderMethod {
16381636
return buildParameterizedTestDataProviderMethod {
16391637
name = dataProviderMethodName
1640-
returnType = argListClassId()
1638+
returnType = argListClassId
16411639
statements = block(body)
16421640
// Exceptions and annotations assignment must run after the statements block is build,
16431641
// because we collect info about exceptions and required annotations while building the statements
1644-
exceptions = collectedExceptions
1645-
annotations = createDataProviderAnnotations(dataProviderMethodName)
1642+
exceptions += collectedExceptions
1643+
annotations += createDataProviderAnnotations(dataProviderMethodName)
16461644
}
16471645
}
16481646

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

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,18 @@ import org.utbot.framework.codegen.model.constructor.context.CgContext
66
import org.utbot.framework.codegen.model.constructor.context.CgContextOwner
77
import org.utbot.framework.codegen.model.constructor.util.CgComponents
88
import org.utbot.framework.codegen.model.constructor.util.CgStatementConstructor
9+
import org.utbot.framework.codegen.model.constructor.util.arrayInitializer
910
import org.utbot.framework.codegen.model.constructor.util.get
1011
import org.utbot.framework.codegen.model.constructor.util.isDefaultValueOf
1112
import org.utbot.framework.codegen.model.constructor.util.isNotDefaultValueOf
1213
import org.utbot.framework.codegen.model.constructor.util.typeCast
1314
import org.utbot.framework.codegen.model.tree.CgAllocateArray
14-
import org.utbot.framework.codegen.model.tree.CgAllocateInitializedArray
1515
import org.utbot.framework.codegen.model.tree.CgDeclaration
1616
import org.utbot.framework.codegen.model.tree.CgEnumConstantAccess
1717
import org.utbot.framework.codegen.model.tree.CgExpression
1818
import org.utbot.framework.codegen.model.tree.CgFieldAccess
1919
import org.utbot.framework.codegen.model.tree.CgGetJavaClass
2020
import org.utbot.framework.codegen.model.tree.CgLiteral
21-
import org.utbot.framework.codegen.model.tree.CgNotNullAssertion
2221
import org.utbot.framework.codegen.model.tree.CgStaticFieldAccess
2322
import org.utbot.framework.codegen.model.tree.CgValue
2423
import org.utbot.framework.codegen.model.tree.CgVariable
@@ -225,7 +224,14 @@ internal class CgVariableConstructor(val context: CgContext) :
225224
val canInitWithValues = elementModels.all { it is UtPrimitiveModel } || elementModels.all { it is UtNullModel }
226225

227226
val initializer = if (canInitWithValues) {
228-
CgAllocateInitializedArray(arrayModel)
227+
val elements = elementModels.map { model ->
228+
when (model) {
229+
is UtPrimitiveModel -> model.value.resolve()
230+
is UtNullModel -> null.resolve()
231+
else -> error("Non primitive or null model $model is unexpected in array initializer")
232+
}
233+
}
234+
arrayInitializer(arrayModel.classId, elementType, elements)
229235
} else {
230236
CgAllocateArray(arrayModel.classId, elementType, arrayModel.length)
231237
}

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

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ import org.utbot.framework.plugin.api.util.shortClassId
4242
import org.utbot.framework.plugin.api.util.underlyingType
4343
import kotlinx.collections.immutable.PersistentList
4444
import kotlinx.collections.immutable.PersistentSet
45+
import org.utbot.framework.codegen.model.tree.CgAllocateInitializedArray
46+
import org.utbot.framework.codegen.model.tree.CgArrayInitializer
47+
import org.utbot.framework.plugin.api.util.arrayLikeName
4548

4649
internal data class EnvironmentFieldStateCache(
4750
val thisInstance: FieldStateCache,
@@ -216,6 +219,39 @@ internal fun CgContextOwner.typeCast(
216219
return CgTypeCast(targetType, expression, isSafetyCast)
217220
}
218221

222+
@Suppress("unused")
223+
internal fun newArrayOf(elementType: ClassId, values: List<CgExpression>): CgAllocateInitializedArray {
224+
val arrayType = arrayTypeOf(elementType)
225+
return CgAllocateInitializedArray(arrayInitializer(arrayType, elementType, values))
226+
}
227+
228+
internal fun arrayInitializer(arrayType: ClassId, elementType: ClassId, values: List<CgExpression>): CgArrayInitializer =
229+
CgArrayInitializer(arrayType, elementType, values)
230+
231+
/**
232+
* For a given [elementType] returns a [ClassId] of an array with elements of this type.
233+
* For example, for an id of `int` the result will be an id of `int[]`.
234+
*
235+
* @param elementType the element type of the returned array class id
236+
* @param isNullable a flag whether returned array is nullable or not
237+
*/
238+
internal fun arrayTypeOf(elementType: ClassId, isNullable: Boolean = false): ClassId {
239+
val arrayIdName = "[${elementType.arrayLikeName}"
240+
return when (elementType) {
241+
is BuiltinClassId -> BuiltinClassId(
242+
name = arrayIdName,
243+
canonicalName = "${elementType.canonicalName}[]",
244+
simpleName = "${elementType.simpleName}[]",
245+
isNullable = isNullable
246+
)
247+
else -> ClassId(
248+
name = arrayIdName,
249+
elementClassId = elementType,
250+
isNullable = isNullable
251+
)
252+
}
253+
}
254+
219255
@Suppress("unused")
220256
internal fun CgContextOwner.getJavaClass(classId: ClassId): CgGetClass {
221257
importIfNeeded(classId)

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,8 @@ class CgParameterizedTestDataProviderBuilder : CgMethodBuilder<CgParameterizedTe
100100
override lateinit var returnType: ClassId
101101
override val parameters: List<CgParameterDeclaration> = mutableListOf()
102102
override lateinit var statements: List<CgStatement>
103-
override lateinit var annotations: MutableList<CgAnnotation>
104-
override lateinit var exceptions: MutableSet<ClassId>
103+
override val annotations: MutableList<CgAnnotation> = mutableListOf()
104+
override val exceptions: MutableSet<ClassId> = mutableSetOf()
105105
override var documentation: CgDocumentationComment = CgDocumentationComment(emptyList())
106106

107107
override fun build() = CgParameterizedTestDataProviderMethod(name, statements, returnType, annotations, exceptions)

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

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import org.utbot.framework.plugin.api.ExecutableId
1919
import org.utbot.framework.plugin.api.FieldId
2020
import org.utbot.framework.plugin.api.MethodId
2121
import org.utbot.framework.plugin.api.TypeParameters
22-
import org.utbot.framework.plugin.api.UtArrayModel
2322
import org.utbot.framework.plugin.api.util.booleanClassId
2423
import org.utbot.framework.plugin.api.util.id
2524
import org.utbot.framework.plugin.api.util.intClassId
@@ -83,6 +82,7 @@ interface CgElement {
8382
is CgNonStaticRunnable -> visit(element)
8483
is CgStaticRunnable -> visit(element)
8584
is CgAllocateInitializedArray -> visit(element)
85+
is CgArrayInitializer -> visit(element)
8686
is CgAllocateArray -> visit(element)
8787
is CgEnumConstantAccess -> visit(element)
8888
is CgFieldAccess -> visit(element)
@@ -699,8 +699,19 @@ open class CgAllocateArray(type: ClassId, elementType: ClassId, val size: Int) :
699699
}
700700
}
701701

702-
class CgAllocateInitializedArray(val model: UtArrayModel) :
703-
CgAllocateArray(model.classId, model.classId.elementClassId!!, model.length)
702+
/**
703+
* Allocation of an array with initialization. For example: `new String[] {"a", "b", null}`.
704+
*/
705+
class CgAllocateInitializedArray(val initializer: CgArrayInitializer) :
706+
CgAllocateArray(initializer.arrayType, initializer.elementType, initializer.size)
707+
708+
class CgArrayInitializer(val arrayType: ClassId, val elementType: ClassId, val values: List<CgExpression>) : CgExpression {
709+
val size: Int
710+
get() = values.size
711+
712+
override val type: ClassId
713+
get() = arrayType
714+
}
704715

705716

706717
// Spread operator (for Kotlin, empty for Java)

0 commit comments

Comments
 (0)