Skip to content

Commit 31ca4c4

Browse files
committed
Apply fixes to mocks creation in specific implementors modes, add documentation.
1 parent 1c1305a commit 31ca4c4

File tree

3 files changed

+62
-36
lines changed

3 files changed

+62
-36
lines changed

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1157,18 +1157,20 @@ class WildcardTypeParameter : TypeParameters(emptyList())
11571157
*/
11581158
enum class TypeReplacementMode {
11591159
/**
1160-
* Any possible implementor may be used.
1160+
* Any possible implementor (that is preferred by solver) may be used.
11611161
*/
11621162
AnyImplementor,
11631163

11641164
/**
11651165
* There is a known implementor to be used.
1166-
* For example, it is found in Spring bean definitions.
1166+
* For example, it is obtained from bean definitions in Spring application.
11671167
*/
11681168
KnownImplementor,
11691169

11701170
/**
11711171
* Using implementors is not allowed.
1172+
* If mocking is allowed, mock of this type will be used.
1173+
* Otherwise, branch will be pruned as unsatisfiable.
11721174
*/
11731175
NoImplementors,
11741176
}

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

Lines changed: 57 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1376,14 +1376,19 @@ class Traverser(
13761376
): ObjectValue {
13771377
touchAddress(addr)
13781378

1379+
// Some types (e.g., interfaces) need to be mocked or replaced with the concrete implementor.
1380+
// Typically, this implementor is selected by SMT solver later.
1381+
// However, if we have the restriction on implementor type (it may be obtained
1382+
// from Spring bean definitions, for example), we can just create a symbolic object
1383+
// with hard constraint on the mentioned type.
13791384
val replacedClassId = when (applicationContext.typeReplacementMode) {
13801385
KnownImplementor -> applicationContext.replaceTypeIfNeeded(type)
13811386
AnyImplementor,
13821387
NoImplementors -> null
13831388
}
13841389

13851390
replacedClassId?.let {
1386-
val sootType = typeResolver.classOrDefault(it.canonicalName)
1391+
val sootType = Scene.v().getSootClass(it.canonicalName).type
13871392
val typeStorage = typeResolver.constructTypeStorage(sootType, useConcreteType = false)
13881393

13891394
val typeHardConstraint = typeRegistry.typeConstraint(addr, typeStorage).all().asHardConstraint()
@@ -1494,36 +1499,7 @@ class Traverser(
14941499
"but there is no mock info generator provided to construct a mock value."
14951500
}
14961501

1497-
val mockInfo = mockInfoGenerator.generate(addr)
1498-
val mockedObjectInfo = mocker.forceMock(type, mockInfoGenerator.generate(addr))
1499-
1500-
val mockedObject: ObjectValue = when (mockedObjectInfo) {
1501-
is NoMock -> error("Value must be mocked after the force mock")
1502-
is ExpectedMock -> mockedObjectInfo.value
1503-
is UnexpectedMock -> {
1504-
// if mock occurs, but it is unexpected due to some reasons
1505-
// (e.g. we do not have mock framework installed),
1506-
// we can only generate a test that uses null value for mocked object
1507-
queuedSymbolicStateUpdates += nullEqualityConstraint.asHardConstraint()
1508-
1509-
mockedObjectInfo.value
1510-
}
1511-
}
1512-
1513-
if (mockedObjectInfo is UnexpectedMock) {
1514-
return mockedObject
1515-
}
1516-
1517-
queuedSymbolicStateUpdates += MemoryUpdate(mockInfos = persistentListOf(MockInfoEnriched(mockInfo)))
1518-
1519-
// add typeConstraint for mocked object. It's a declared type of the object.
1520-
val typeConstraint = typeRegistry.typeConstraint(addr, mockedObject.typeStorage).all()
1521-
val isMockConstraint = mkEq(typeRegistry.isMock(mockedObject.addr), UtTrue)
1522-
1523-
queuedSymbolicStateUpdates += typeConstraint.asHardConstraint()
1524-
queuedSymbolicStateUpdates += mkOr(isMockConstraint, nullEqualityConstraint).asHardConstraint()
1525-
1526-
return mockedObject
1502+
return createMockedObject(addr, type, mockInfoGenerator)
15271503
}
15281504

15291505
val concreteImplementation = when (applicationContext.typeReplacementMode) {
@@ -1538,10 +1514,19 @@ class Traverser(
15381514
// Otherwise we'd have either a wrong type in the resolver, or missing method like 'preconditionCheck'.
15391515
wrapperToClass[type]?.first()?.let { wrapper(it, addr) }?.concrete
15401516
}
1541-
// In case of known implementor we should have already tried to replace type using `replaceTypeIfNeeded`.
1517+
// In case of `KnownImplementor` mode we should have already tried to replace type using `replaceTypeIfNeeded`.
1518+
// However, this replacement attempt might be unsuccessful even if some possible concrete types are present.
1519+
// For example, we may have two concrete implementors present in Spring bean definitions, so we do not know
1520+
// which one to use. In such case we try to mock this type, if it is possible, or prune branch as unsatisfiable.
1521+
//
1522+
// In case of `NoImplementors` mode we should try to mock this type or prune branch as unsatisfiable.
1523+
// Mocking can be impossible here as there are no guaranties that `mockInfoGenerator` is instantiated.
15421524
KnownImplementor,
1543-
// If no implementors are allowed, we should have already generated a mock object.
15441525
NoImplementors -> {
1526+
mockInfoGenerator?.let {
1527+
return createMockedObject(addr, type, it)
1528+
}
1529+
15451530
queuedSymbolicStateUpdates += mkFalse().asHardConstraint()
15461531
null
15471532
}
@@ -1550,6 +1535,45 @@ class Traverser(
15501535
return ObjectValue(typeStorage, addr, concreteImplementation)
15511536
}
15521537

1538+
private fun createMockedObject(
1539+
addr: UtAddrExpression,
1540+
type: RefType,
1541+
mockInfoGenerator: UtMockInfoGenerator,
1542+
): ObjectValue {
1543+
val nullEqualityConstraint = mkEq(addr, nullObjectAddr)
1544+
1545+
val mockInfo = mockInfoGenerator.generate(addr)
1546+
val mockedObjectInfo = mocker.forceMock(type, mockInfoGenerator.generate(addr))
1547+
1548+
val mockedObject: ObjectValue = when (mockedObjectInfo) {
1549+
is NoMock -> error("Value must be mocked after the force mock")
1550+
is ExpectedMock -> mockedObjectInfo.value
1551+
is UnexpectedMock -> {
1552+
// if mock occurs, but it is unexpected due to some reasons
1553+
// (e.g. we do not have mock framework installed),
1554+
// we can only generate a test that uses null value for mocked object
1555+
queuedSymbolicStateUpdates += nullEqualityConstraint.asHardConstraint()
1556+
1557+
mockedObjectInfo.value
1558+
}
1559+
}
1560+
1561+
if (mockedObjectInfo is UnexpectedMock) {
1562+
return mockedObject
1563+
}
1564+
1565+
queuedSymbolicStateUpdates += MemoryUpdate(mockInfos = persistentListOf(MockInfoEnriched(mockInfo)))
1566+
1567+
// add typeConstraint for mocked object. It's a declared type of the object.
1568+
val typeConstraint = typeRegistry.typeConstraint(addr, mockedObject.typeStorage).all()
1569+
val isMockConstraint = mkEq(typeRegistry.isMock(mockedObject.addr), UtTrue)
1570+
1571+
queuedSymbolicStateUpdates += typeConstraint.asHardConstraint()
1572+
queuedSymbolicStateUpdates += mkOr(isMockConstraint, nullEqualityConstraint).asHardConstraint()
1573+
1574+
return mockedObject
1575+
}
1576+
15531577
private fun TraversalContext.resolveConstant(constant: Constant): SymbolicValue =
15541578
when (constant) {
15551579
is IntConstant -> constant.value.toPrimitiveValue()

utbot-framework/src/main/kotlin/org/utbot/engine/types/TypeResolver.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ class TypeResolver(private val typeRegistry: TypeRegistry, private val hierarchy
9595
.toSet()
9696
}
9797

98-
fun classOrDefault(typeName: String): RefType =
98+
private fun classOrDefault(typeName: String): RefType =
9999
runCatching { Scene.v().getRefType(typeName) }.getOrDefault(OBJECT_TYPE)
100100

101101
fun findFields(type: RefType) = typeRegistry.findFields(type) {

0 commit comments

Comments
 (0)