Skip to content

Commit a8672d0

Browse files
authored
Replace all Spring specific models with models expressed in terms of UtSpringContextModel (#2279)
1 parent df5bba9 commit a8672d0

File tree

18 files changed

+180
-200
lines changed

18 files changed

+180
-200
lines changed

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

Lines changed: 23 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ import org.utbot.common.isAbstract
5959
import org.utbot.common.isStatic
6060
import org.utbot.framework.isFromTrustedLibrary
6161
import org.utbot.framework.plugin.api.TypeReplacementMode.*
62+
import org.utbot.framework.plugin.api.util.SpringModelUtils
6263
import org.utbot.framework.plugin.api.util.allDeclaredFieldIds
6364
import org.utbot.framework.plugin.api.util.fieldId
6465
import org.utbot.framework.plugin.api.util.isSubtypeOf
@@ -657,44 +658,16 @@ class UtLambdaModel(
657658
}
658659
}
659660

660-
abstract class UtAutowiredBaseModel(
661-
override val id: Int?,
662-
override val classId: ClassId,
663-
val origin: UtModel,
664-
modelName: String
665-
) : UtReferenceModel(
666-
id, classId, modelName
661+
class UtSpringContextModel : UtReferenceModel(
662+
id = null,
663+
classId = SpringModelUtils.applicationContextClassId,
664+
modelName = "applicationContext"
667665
)
668666

669-
class UtAutowiredStateBeforeModel(
670-
id: Int?,
671-
classId: ClassId,
672-
origin: UtModel,
673-
val beanName: String,
674-
val repositoriesContent: List<RepositoryContentModel>,
675-
) : UtAutowiredBaseModel(
676-
id, classId, origin, modelName = "@Autowired $beanName#$id"
677-
)
678-
679-
data class RepositoryContentModel(
667+
data class SpringRepositoryId(
680668
val repositoryBeanName: String,
681-
val entityModels: List<UtModel>,
682-
)
683-
684-
class UtAutowiredStateAfterModel(
685-
id: Int?,
686-
classId: ClassId,
687-
origin: UtModel,
688-
val repositoryInteractions: List<RepositoryInteractionModel>,
689-
) : UtAutowiredBaseModel(
690-
id, classId, origin, modelName = "@Autowired ${classId.name}#$id"
691-
)
692-
693-
data class RepositoryInteractionModel(
694-
val beanName: String,
695-
val executableId: ExecutableId,
696-
val args: List<UtModel>,
697-
val result: UtExecutionResult
669+
val repositoryClassId: ClassId,
670+
val entityClassId: ClassId,
698671
)
699672

700673
/**
@@ -1146,6 +1119,14 @@ sealed class ExecutableId : StatementId() {
11461119
abstract val returnType: ClassId
11471120
abstract val parameters: List<ClassId>
11481121

1122+
/**
1123+
* Normally during concrete execution every executable is executed in a
1124+
* [sandbox](https://github.com/UnitTestBot/UTBotJava/blob/main/docs/Sandboxing.md).
1125+
*
1126+
* However, if this flag is set to `true`, then `this` particular executable is executed without a sandbox.
1127+
*/
1128+
abstract val bypassesSandbox: Boolean
1129+
11491130
abstract val modifiers: Int
11501131

11511132
val signature: String
@@ -1186,14 +1167,16 @@ open class MethodId(
11861167
override val name: String,
11871168
override val returnType: ClassId,
11881169
override val parameters: List<ClassId>,
1170+
override val bypassesSandbox: Boolean = false,
11891171
) : ExecutableId() {
11901172
override val modifiers: Int
11911173
get() = method.modifiers
11921174
}
11931175

11941176
open class ConstructorId(
11951177
override val classId: ClassId,
1196-
override val parameters: List<ClassId>
1178+
override val parameters: List<ClassId>,
1179+
override val bypassesSandbox: Boolean = false,
11971180
) : ExecutableId() {
11981181
final override val name: String = "<init>"
11991182
final override val returnType: ClassId = voidClassId
@@ -1208,12 +1191,13 @@ class BuiltinMethodId(
12081191
name: String,
12091192
returnType: ClassId,
12101193
parameters: List<ClassId>,
1194+
bypassesSandbox: Boolean = false,
12111195
// by default we assume that the builtin method is non-static and public
12121196
isStatic: Boolean = false,
12131197
isPublic: Boolean = true,
12141198
isProtected: Boolean = false,
12151199
isPrivate: Boolean = false
1216-
) : MethodId(classId, name, returnType, parameters) {
1200+
) : MethodId(classId, name, returnType, parameters, bypassesSandbox) {
12171201
override val modifiers: Int = ModifierFactory {
12181202
static = isStatic
12191203
public = isPublic
@@ -1225,11 +1209,12 @@ class BuiltinMethodId(
12251209
class BuiltinConstructorId(
12261210
classId: ClassId,
12271211
parameters: List<ClassId>,
1212+
bypassesSandbox: Boolean = false,
12281213
// by default, we assume that the builtin constructor is public
12291214
isPublic: Boolean = true,
12301215
isProtected: Boolean = false,
12311216
isPrivate: Boolean = false
1232-
) : ConstructorId(classId, parameters) {
1217+
) : ConstructorId(classId, parameters, bypassesSandbox) {
12331218
override val modifiers: Int = ModifierFactory {
12341219
public = isPublic
12351220
private = isPrivate

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ fun Class<*>.singleConstructor(signature: String): Constructor<*> =
7676

7777
fun Class<*>.singleMethodOrNull(signature: String): Method? =
7878
generateSequence(this) { it.superclass }.mapNotNull { clazz ->
79-
clazz.declaredMethods.firstOrNull { it.signature == signature }
79+
(clazz.methods + clazz.declaredMethods).firstOrNull { it.signature == signature }
8080
}.firstOrNull()
8181

8282
/**
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package org.utbot.framework.plugin.api.util
2+
3+
import org.utbot.framework.plugin.api.ClassId
4+
import org.utbot.framework.plugin.api.MethodId
5+
import org.utbot.framework.plugin.api.SpringRepositoryId
6+
import org.utbot.framework.plugin.api.UtAssembleModel
7+
import org.utbot.framework.plugin.api.UtExecutableCallModel
8+
import org.utbot.framework.plugin.api.UtModel
9+
import org.utbot.framework.plugin.api.UtPrimitiveModel
10+
import org.utbot.framework.plugin.api.UtSpringContextModel
11+
12+
object SpringModelUtils {
13+
val applicationContextClassId = ClassId("org.springframework.context.ApplicationContext")
14+
val crudRepositoryClassId = ClassId("org.springframework.data.repository.CrudRepository")
15+
16+
val getBeanMethodId = MethodId(
17+
classId = applicationContextClassId,
18+
name = "getBean",
19+
returnType = Any::class.id,
20+
parameters = listOf(String::class.id),
21+
bypassesSandbox = true // TODO may be we can use some alternative sandbox that has more permissions
22+
)
23+
24+
val saveMethodId = MethodId(
25+
classId = crudRepositoryClassId,
26+
name = "save",
27+
returnType = Any::class.id,
28+
parameters = listOf(Any::class.id)
29+
)
30+
31+
32+
fun createBeanModel(beanName: String, id: Int, classId: ClassId) = UtAssembleModel(
33+
id = id,
34+
classId = classId,
35+
modelName = "@Autowired $beanName",
36+
instantiationCall = UtExecutableCallModel(
37+
instance = UtSpringContextModel(),
38+
executable = getBeanMethodId,
39+
params = listOf(UtPrimitiveModel(beanName))
40+
),
41+
modificationsChainProvider = { mutableListOf() }
42+
)
43+
44+
fun createSaveCallModel(repositoryId: SpringRepositoryId, id: Int, entityModel: UtModel) = UtExecutableCallModel(
45+
instance = createBeanModel(
46+
beanName = repositoryId.repositoryBeanName,
47+
id = id,
48+
classId = repositoryId.repositoryClassId,
49+
),
50+
executable = saveMethodId,
51+
params = listOf(entityModel)
52+
)
53+
}

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

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -386,21 +386,25 @@ class UtBotSymbolicEngine(
386386
.letIf(applicationContext is SpringApplicationContext
387387
&& applicationContext.typeReplacementApproach is TypeReplacementApproach.ReplaceIfPossible
388388
) { provider ->
389+
// TODO #2274 properly detect relevant repositories (right now orderRepository is hardcoded)
390+
val relevantRepositories = listOf(
391+
SpringRepositoryId(
392+
repositoryBeanName = "orderRepository",
393+
repositoryClassId = ClassId("com.rest.order.repositories.OrderRepository"),
394+
entityClassId = ClassId("com.rest.order.models.Order")
395+
)
396+
)
397+
logger.info { "Relevant repositories: $relevantRepositories" }
389398
// spring should try to generate bean values, but if it fails, then object value provider is used for it
390399
val springBeanValueProvider = SpringBeanValueProvider(
391400
defaultIdGenerator,
392-
beanProvider = { classId ->
401+
beanNameProvider = { classId ->
393402
(applicationContext as SpringApplicationContext).beanDefinitions
394403
.filter { it.beanTypeFqn == classId.name }
395404
.map { it.beanName }
396405
},
397-
autowiredModelOriginCreator = { beanName ->
398-
runBlocking {
399-
logger.info { "Getting bean: $beanName" }
400-
concreteExecutor.withProcess { getBean(beanName) }
401-
}
402-
}).withFallback(ObjectValueProvider(defaultIdGenerator))
403-
406+
relevantRepositories = relevantRepositories
407+
).withFallback(ObjectValueProvider(defaultIdGenerator))
404408
provider.except { p -> p is ObjectValueProvider }.with(springBeanValueProvider)
405409
}.let(transform)
406410
runJavaFuzzing(

utbot-framework/src/main/kotlin/org/utbot/framework/assemble/AssembleModelGenerator.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ class AssembleModelGenerator(private val basePackageName: String) {
204204
is UtArrayModel -> assembleArrayModel(utModel)
205205
is UtCompositeModel -> assembleCompositeModel(utModel)
206206
is UtAssembleModel -> assembleAssembleModel(utModel)
207-
// Python, JavaScript are supposed to be here as well
207+
// Python, JavaScript, UtSpringContextModel are supposed to be here as well
208208
else -> utModel
209209
}
210210
} catch (e: AssembleException) {

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/builders/SpringTestClassModelBuilder.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import org.utbot.framework.plugin.api.UtLambdaModel
1616
import org.utbot.framework.plugin.api.UtModel
1717
import org.utbot.framework.plugin.api.UtNullModel
1818
import org.utbot.framework.plugin.api.UtPrimitiveModel
19+
import org.utbot.framework.plugin.api.UtSpringContextModel
1920
import org.utbot.framework.plugin.api.UtStatementCallModel
2021
import org.utbot.framework.plugin.api.UtVoidModel
2122
import org.utbot.framework.plugin.api.isMockModel
@@ -95,7 +96,8 @@ class SpringTestClassModelBuilder(val context: CgContext): TestClassModelBuilder
9596
is UtPrimitiveModel,
9697
is UtClassRefModel,
9798
is UtVoidModel,
98-
is UtEnumConstantModel -> {}
99+
is UtEnumConstantModel,
100+
is UtSpringContextModel -> {}
99101
is UtLambdaModel -> {
100102
currentModel.capturedValues.forEach { collectRecursively(it, allModels) }
101103
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.utbot.framework.codegen.tree
22

3+
import mu.KotlinLogging
34
import org.utbot.common.WorkaroundReason
45
import org.utbot.common.isStatic
56
import org.utbot.common.workaround
@@ -162,6 +163,10 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte
162163
CgCallableAccessManager by getCallableAccessManagerBy(context),
163164
CgStatementConstructor by getStatementConstructorBy(context) {
164165

166+
companion object {
167+
private val logger = KotlinLogging.logger {}
168+
}
169+
165170
protected val nameGenerator = getNameGeneratorBy(context)
166171
protected val testFrameworkManager = getTestFrameworkManagerBy(context)
167172

@@ -470,6 +475,8 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte
470475
neededStackTraceLines += TAB + line
471476
}
472477
}
478+
if (!executableCallFound)
479+
logger.warn(exception) { "Failed to find executable call in stack trace" }
473480

474481
+CgMultilineComment(warningLine + neededStackTraceLines.reversed())
475482
}

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

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,36 +5,19 @@ import org.utbot.framework.codegen.domain.context.CgContext
55
import org.utbot.framework.codegen.domain.models.CgValue
66
import org.utbot.framework.codegen.domain.models.CgVariable
77
import org.utbot.framework.plugin.api.UtAssembleModel
8-
import org.utbot.framework.plugin.api.UtAutowiredBaseModel
9-
import org.utbot.framework.plugin.api.UtAutowiredStateBeforeModel
108
import org.utbot.framework.plugin.api.UtCompositeModel
119
import org.utbot.framework.plugin.api.UtModel
10+
import org.utbot.framework.plugin.api.UtSpringContextModel
1211
import org.utbot.framework.plugin.api.isMockModel
13-
import org.utbot.framework.plugin.api.util.jClass
1412

1513
class CgSpringVariableConstructor(context: CgContext) : CgVariableConstructor(context) {
1614
val injectedMocksModelsVariables: MutableSet<UtModelWrapper> = mutableSetOf()
1715
val mockedModelsVariables: MutableSet<UtModelWrapper> = mutableSetOf()
1816

1917
override fun getOrCreateVariable(model: UtModel, name: String?): CgValue {
20-
if (model is UtAutowiredBaseModel) {
18+
if (model is UtSpringContextModel) {
2119
// TODO also, properly render corresponding @Autowired field(s), and make sure name isn't taken
22-
if (model is UtAutowiredStateBeforeModel) {
23-
comment("begin repository fill up")
24-
model.repositoriesContent.forEach { repositoryContent ->
25-
repositoryContent.entityModels.forEach { entity ->
26-
// TODO actually fill up repositories
27-
getOrCreateVariable(entity)
28-
emptyLine()
29-
}
30-
}
31-
comment("end repository fill up")
32-
}
33-
emptyLine()
34-
return CgVariable(
35-
name ?: model.classId.jClass.simpleName.let { it[0].lowercase() + it.drop(1) },
36-
model.classId
37-
)
20+
return CgVariable(model.modelName, model.classId)
3821
}
3922

4023
val alreadyCreatedInjectMocks = findCgValueByModel(model, injectedMocksModelsVariables)

utbot-framework/src/main/kotlin/org/utbot/framework/fields/ExecutionStateAnalyzer.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import org.utbot.framework.plugin.api.ClassId
88
import org.utbot.framework.plugin.api.MissingState
99
import org.utbot.framework.plugin.api.UtArrayModel
1010
import org.utbot.framework.plugin.api.UtAssembleModel
11-
import org.utbot.framework.plugin.api.UtAutowiredBaseModel
1211
import org.utbot.framework.plugin.api.UtClassRefModel
1312
import org.utbot.framework.plugin.api.UtCompositeModel
1413
import org.utbot.framework.plugin.api.UtEnumConstantModel
@@ -18,6 +17,7 @@ import org.utbot.framework.plugin.api.UtModel
1817
import org.utbot.framework.plugin.api.UtNullModel
1918
import org.utbot.framework.plugin.api.UtPrimitiveModel
2019
import org.utbot.framework.plugin.api.UtReferenceModel
20+
import org.utbot.framework.plugin.api.UtSpringContextModel
2121
import org.utbot.framework.plugin.api.UtSymbolicExecution
2222
import org.utbot.framework.plugin.api.UtVoidModel
2323
import org.utbot.framework.util.UtModelVisitor
@@ -238,8 +238,8 @@ private class FieldStateVisitor : UtModelVisitor<FieldData>() {
238238
recordFieldState(data, element)
239239
}
240240

241-
override fun visit(element: UtAutowiredBaseModel, data: FieldData) {
242-
element.origin.accept(this, data)
241+
override fun visit(element: UtSpringContextModel, data: FieldData) {
242+
recordFieldState(data, element)
243243
}
244244

245245
private fun recordFieldState(data: FieldData, model: UtModel) {

utbot-framework/src/main/kotlin/org/utbot/framework/minimization/Minimization.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ private fun UtModel.calculateSize(used: MutableSet<UtModel> = mutableSetOf()): I
235235
}
236236
is UtCompositeModel -> 1 + fields.values.sumOf { it.calculateSize(used) }
237237
is UtLambdaModel -> 1 + capturedValues.sumOf { it.calculateSize(used) }
238-
// PythonModel, JsUtModel may be here
238+
// PythonModel, JsUtModel, UtSpringContextModel may be here
239239
else -> 0
240240
}
241241
}

0 commit comments

Comments
 (0)