Skip to content

Commit b5cebbd

Browse files
authored
Merge branch 'main' into kononov-rider-fixes
2 parents 923ce92 + 22badb4 commit b5cebbd

File tree

6 files changed

+108
-37
lines changed

6 files changed

+108
-37
lines changed

.github/pull_request_template.md

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,11 @@ Check that the title contains
2525

2626
## Description
2727

28-
- Mention the related issue (_**"Fixes #..."**_) or describe why you've made the changes.
29-
- Add useful info for your reviewer or another contributor.
28+
Fixes # (issue)
29+
30+
Add more info _if needed_:
31+
* context/purpose for implementing changes
32+
* detailed description of the changes made
3033

3134
## How to test
3235

@@ -46,11 +49,13 @@ If this is your case, share the detailed _manual scenarios_ that help to verify
4649

4750
## Self-check list
4851

49-
Check off the item if the statement is true:
52+
Check off the item if the statement is true. Hint: [x] is a marked item.
53+
54+
Please do not delete the list or its items.
5055

5156
- [ ] I've set the proper **labels** for my PR (at least, for category and component).
5257
- [ ] PR **title** and **description** are clear and intelligible.
53-
- [ ] I've added enough **comments**, particularly in hard-to-understand areas.
58+
- [ ] I've added enough **comments** to my code, particularly in hard-to-understand areas.
5459
- [ ] The functionality I've repaired, changed or added is covered with **automated tests**.
5560
- [ ] **Manual tests** have been provided optionally.
5661
- [ ] The **documentation** for the functionality I've been working on is up-to-date.

utbot-framework-test/src/test/kotlin/org/utbot/examples/enums/ClassWithEnumTest.kt

Lines changed: 33 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
package org.utbot.examples.enums
22

3+
import org.junit.jupiter.api.Disabled
34
import org.utbot.examples.enums.ClassWithEnum.StatusEnum.ERROR
45
import org.utbot.examples.enums.ClassWithEnum.StatusEnum.READY
56
import org.utbot.framework.plugin.api.FieldId
67
import org.utbot.framework.plugin.api.util.id
7-
import org.junit.jupiter.api.Disabled
88
import org.junit.jupiter.api.Test
9+
import org.utbot.examples.enums.ClassWithEnum.StatusEnum
910
import org.utbot.framework.plugin.api.util.jField
1011
import org.utbot.testcheckers.eq
1112
import org.utbot.testcheckers.withPushingStateFromPathSelectorForConcrete
1213
import org.utbot.testcheckers.withoutConcrete
1314
import org.utbot.testing.DoNotCalculate
1415
import org.utbot.testing.UtValueTestCaseChecker
16+
import org.utbot.testing.between
1517
import org.utbot.testing.ignoreExecutionsNumber
1618
import org.utbot.testing.isException
1719

@@ -44,19 +46,16 @@ class ClassWithEnumTest : UtValueTestCaseChecker(testClass = ClassWithEnum::clas
4446
}
4547

4648
@Test
47-
@Disabled("TODO JIRA:1686")
4849
fun testNullParameter() {
4950
check(
5051
ClassWithEnum::nullEnumAsParameter,
51-
eq(3),
52+
between(2..3),
5253
{ e, _ -> e == null },
53-
{ e, r -> e == READY && r == 0 },
54-
{ e, r -> e == ERROR && r == -1 },
54+
{ e, r -> e == READY && r == 0 || e == ERROR && r == -1 },
5555
)
5656
}
5757

5858
@Test
59-
@Disabled("TODO JIRA:1686")
6059
fun testNullField() {
6160
checkWithException(
6261
ClassWithEnum::nullField,
@@ -67,22 +66,21 @@ class ClassWithEnumTest : UtValueTestCaseChecker(testClass = ClassWithEnum::clas
6766
)
6867
}
6968

69+
@Suppress("KotlinConstantConditions")
7070
@Test
71-
@Disabled("TODO JIRA:1686")
7271
fun testChangeEnum() {
7372
checkWithException(
7473
ClassWithEnum::changeEnum,
75-
eq(3),
76-
{ e, r -> e == null && r.isException<NullPointerException>() },
74+
eq(2),
7775
{ e, r -> e == READY && r.getOrNull()!! == ERROR.ordinal },
78-
{ e, r -> e == ERROR && r.getOrNull()!! == READY.ordinal },
76+
{ e, r -> (e == ERROR || e == null) && r.getOrNull()!! == READY.ordinal },
7977
)
8078
}
8179

8280
@Test
8381
fun testChangeMutableField() {
8482
// TODO testing code generation for this method is disabled because we need to restore original field state
85-
// should be enabled after solving JIRA:1648
83+
// should be enabled after solving https://github.com/UnitTestBot/UTBotJava/issues/80
8684
withEnabledTestingCodeGeneration(testCodeGeneration = false) {
8785
checkWithException(
8886
ClassWithEnum::changeMutableField,
@@ -94,7 +92,7 @@ class ClassWithEnumTest : UtValueTestCaseChecker(testClass = ClassWithEnum::clas
9492
}
9593

9694
@Test
97-
@Disabled("TODO JIRA:1686")
95+
@Disabled("https://github.com/UnitTestBot/UTBotJava/issues/1745")
9896
fun testCheckName() {
9997
check(
10098
ClassWithEnum::checkName,
@@ -111,21 +109,35 @@ class ClassWithEnumTest : UtValueTestCaseChecker(testClass = ClassWithEnum::clas
111109
ClassWithEnum::changingStaticWithEnumInit,
112110
eq(1),
113111
{ t, staticsAfter, r ->
114-
// for some reasons x is inaccessible
115-
val x = FieldId(t.javaClass.id, "x").jField.get(t) as Int
112+
// We cannot check `x` since it is not a meaningful value since
113+
// it is accessed only in a static initializer.
114+
115+
// For some reasons x is inaccessible
116+
// val x = FieldId(t.javaClass.id, "x").jField.get(t) as Int
116117

117118
val y = staticsAfter[FieldId(ClassWithEnum.ClassWithStaticField::class.id, "y")]!!.value as Int
118119

119-
val areStaticsCorrect = x == 1 && y == 11
120+
val areStaticsCorrect = /*x == 1 &&*/ y == 11
120121
areStaticsCorrect && r == true
121122
}
122123
)
123124
}
124125

126+
@Test
127+
fun testVirtualFunction() {
128+
check(
129+
ClassWithEnum::virtualFunction,
130+
eq(3),
131+
{ parameter, _ -> parameter == null },
132+
{ parameter, r -> r == 1 && parameter == ERROR },
133+
{ parameter, r -> r == 0 && parameter == READY },
134+
)
135+
}
136+
125137
@Test
126138
fun testEnumValues() {
127139
checkStaticMethod(
128-
ClassWithEnum.StatusEnum::values,
140+
StatusEnum::values,
129141
eq(1),
130142
{ r -> r.contentEquals(arrayOf(READY, ERROR)) },
131143
)
@@ -134,7 +146,7 @@ class ClassWithEnumTest : UtValueTestCaseChecker(testClass = ClassWithEnum::clas
134146
@Test
135147
fun testFromCode() {
136148
checkStaticMethod(
137-
ClassWithEnum.StatusEnum::fromCode,
149+
StatusEnum::fromCode,
138150
eq(3),
139151
{ code, r -> code == 10 && r == READY },
140152
{ code, r -> code == -10 && r == ERROR },
@@ -145,22 +157,19 @@ class ClassWithEnumTest : UtValueTestCaseChecker(testClass = ClassWithEnum::clas
145157
@Test
146158
fun testFromIsReady() {
147159
checkStaticMethod(
148-
ClassWithEnum.StatusEnum::fromIsReady,
160+
StatusEnum::fromIsReady,
149161
eq(2),
150162
{ isFirst, r -> isFirst && r == READY },
151163
{ isFirst, r -> !isFirst && r == ERROR },
152164
)
153165
}
154166

155167
@Test
156-
@Disabled("TODO JIRA:1450")
157168
fun testPublicGetCodeMethod() {
158169
checkWithThis(
159-
ClassWithEnum.StatusEnum::publicGetCode,
160-
eq(2),
161-
{ enumInstance, r -> enumInstance == READY && r == 10 },
162-
{ enumInstance, r -> enumInstance == ERROR && r == -10 },
163-
coverage = DoNotCalculate
170+
StatusEnum::publicGetCode,
171+
between(1..2),
172+
{ enumInstance, r -> enumInstance == READY && r == 10 || enumInstance == ERROR && r == -10 },
164173
)
165174
}
166175

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

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ import kotlinx.collections.immutable.toPersistentMap
4040
import org.utbot.engine.types.STRING_TYPE
4141
import org.utbot.engine.types.SeqType
4242
import org.utbot.engine.types.TypeResolver
43-
import org.utbot.framework.plugin.api.classId
43+
import org.utbot.framework.plugin.api.id
44+
import org.utbot.framework.plugin.api.util.isEnum
4445
import soot.ArrayType
4546
import soot.CharType
4647
import soot.IntType
@@ -358,7 +359,7 @@ data class Memory( // TODO: split purely symbolic memory and information about s
358359
fun findTypeForArrayOrNull(addr: UtAddrExpression): ArrayType? = addrToArrayType[addr]
359360

360361
fun getSymbolicEnumValues(classId: ClassId): List<ObjectValue> =
361-
symbolicEnumValues.filter { it.type.classId == classId }
362+
extractSymbolicEnumValues(symbolicEnumValues, classId)
362363
}
363364

364365
private fun initialArray(descriptor: MemoryChunkDescriptor) =
@@ -430,7 +431,7 @@ data class MemoryUpdate(
430431
)
431432

432433
fun getSymbolicEnumValues(classId: ClassId): List<ObjectValue> =
433-
symbolicEnumValues.filter { it.type.classId == classId }
434+
extractSymbolicEnumValues(symbolicEnumValues, classId)
434435
}
435436

436437
// array - Java Array
@@ -571,3 +572,21 @@ private operator fun MockInfoEnriched.plus(update: MockInfoEnriched?): MockInfoE
571572
private fun <K, V> MutableMap<K, List<V>>.mergeValues(other: Map<K, List<V>>): Map<K, List<V>> = apply {
572573
other.forEach { (key, values) -> merge(key, values) { v1, v2 -> v1 + v2 } }
573574
}
575+
576+
private fun extractSymbolicEnumValues(
577+
symbolicEnumValuesSource: PersistentList<ObjectValue>,
578+
classId: ClassId
579+
): List<ObjectValue> = symbolicEnumValuesSource.filter {
580+
val symbolicValueClassId = it.type.id
581+
582+
// If symbolicValueClassId is not an enum in accordance with java.lang.Class.isEnum
583+
// function, we have to take results for its superclass (a direct inheritor of java.lang.Enum).
584+
// Otherwise, we should get results by its own classId.
585+
val enumClass = if (symbolicValueClassId.isEnum) {
586+
symbolicValueClassId
587+
} else {
588+
it.type.sootClass.superClassOrNull()?.id
589+
}
590+
591+
enumClass == classId
592+
}

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -558,11 +558,25 @@ class Resolver(
558558
}
559559

560560
val clazz = classLoader.loadClass(sootClass.name)
561-
561+
// If we have an actual type as an Enum instance (a direct inheritor of java.lang.Enum),
562+
// we should construct it using information about corresponding ordinal.
563+
// The right enum constant will be constructed due to constraints added to the solver.
562564
if (clazz.isEnum) {
563565
return constructEnum(concreteAddr, actualType, clazz)
564566
}
565567

568+
// But, if we have the actualType that is not a direct inheritor of java.lang.Enum,
569+
// we have to check its ancestor instead, because we might be in a situation when
570+
// we worked with an enum constant as a parameter, and now we have correctly calculated actual type.
571+
// Since actualType for enums represents its instances and enum constants are not actually
572+
// enum instances (in accordance to java.lang.Class#isEnum), we have to check
573+
// the defaultType below instead on the actual one whether is it an Enum or not.
574+
val defaultClazz = classLoader.loadClass(defaultType.sootClass.name)
575+
576+
if (defaultClazz.isEnum) {
577+
return constructEnum(concreteAddr, actualType, defaultClazz)
578+
}
579+
566580
// check if we have mock with this address
567581
// Unfortunately we cannot do it in constructModel only cause constructTypeOrNull returns null for mocks
568582
val mockInfo = mockInfos[concreteAddr]

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2314,7 +2314,9 @@ class Traverser(
23142314
queuedSymbolicStateUpdates += addrEq(addr, nullObjectAddr).asSoftConstraint()
23152315

23162316
if (type.sootClass.isEnum) {
2317-
createEnum(type, addr)
2317+
// We don't know which enum constant should we create, so we
2318+
// have to create an instance of unknown type to support virtual invokes.
2319+
createEnum(type, addr, useConcreteType = false)
23182320
} else {
23192321
createObject(addr, type, useConcreteType = false, mockInfoGenerator)
23202322
}
@@ -2323,8 +2325,8 @@ class Traverser(
23232325
else -> error("Can't create const from ${type::class}")
23242326
}
23252327

2326-
private fun createEnum(type: RefType, addr: UtAddrExpression): ObjectValue {
2327-
val typeStorage = typeResolver.constructTypeStorage(type, useConcreteType = true)
2328+
private fun createEnum(type: RefType, addr: UtAddrExpression, useConcreteType: Boolean): ObjectValue {
2329+
val typeStorage = typeResolver.constructTypeStorage(type, useConcreteType)
23282330

23292331
queuedSymbolicStateUpdates += typeRegistry.typeConstraint(addr, typeStorage).all().asHardConstraint()
23302332

@@ -2340,6 +2342,7 @@ class Traverser(
23402342
return ObjectValue(typeStorage, addr)
23412343
}
23422344

2345+
@Suppress("SameParameterValue")
23432346
private fun arrayUpdate(array: ArrayValue, index: PrimitiveValue, value: UtExpression): MemoryUpdate {
23442347
val type = array.type
23452348
val chunkId = typeRegistry.arrayChunkId(type)

utbot-sample/src/main/java/org/utbot/examples/enums/ClassWithEnum.java

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,28 @@ public boolean changingStaticWithEnumInit() {
9898
return true;
9999
}
100100

101+
public int virtualFunction(StatusEnum parameter) {
102+
int value = parameter.virtualFunction();
103+
if (value > 0) {
104+
return value;
105+
}
106+
107+
return Math.abs(value);
108+
}
109+
101110
enum StatusEnum {
102-
READY(0, 10, "200"),
103-
ERROR(-1, -10, null);
111+
READY(0, 10, "200") {
112+
@Override
113+
public int virtualFunction() {
114+
return 0;
115+
}
116+
},
117+
ERROR(-1, -10, null) {
118+
@Override
119+
int virtualFunction() {
120+
return 1;
121+
}
122+
};
104123

105124
int mutableInt;
106125
final int code;
@@ -137,6 +156,8 @@ static StatusEnum fromIsReady(boolean isReady) {
137156
int publicGetCode() {
138157
return this == READY ? 10 : -10;
139158
}
159+
160+
abstract int virtualFunction();
140161
}
141162

142163
enum ManyConstantsEnum {

0 commit comments

Comments
 (0)