Skip to content

Commit 53bbc46

Browse files
authored
Automatically detect @GeneratedValue fields (#2312)
1 parent 69796a4 commit 53bbc46

File tree

4 files changed

+38
-17
lines changed

4 files changed

+38
-17
lines changed

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

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,13 @@ import org.utbot.framework.plugin.api.UtSpringContextModel
1212
object SpringModelUtils {
1313
val applicationContextClassId = ClassId("org.springframework.context.ApplicationContext")
1414
val crudRepositoryClassId = ClassId("org.springframework.data.repository.CrudRepository")
15-
val entityClassId = ClassId("javax.persistence.Entity")
15+
16+
// most likely only one persistent library is on the classpath, but we need to be able to work with either of them
17+
private val persistentLibraries = listOf("javax.persistence", "jakarta.persistence")
18+
private fun persistentClassIds(simpleName: String) = persistentLibraries.map { ClassId("$it.$simpleName") }
19+
20+
val entityClassIds = persistentClassIds("Entity")
21+
val generatedValueClassIds = persistentClassIds("GeneratedValue")
1622

1723
private val getBeanMethodId = MethodId(
1824
classId = applicationContextClassId,
@@ -26,7 +32,8 @@ object SpringModelUtils {
2632
classId = crudRepositoryClassId,
2733
name = "save",
2834
returnType = Any::class.id,
29-
parameters = listOf(Any::class.id)
35+
parameters = listOf(Any::class.id),
36+
bypassesSandbox = true // TODO may be we can use some alternative sandbox that has more permissions
3037
)
3138

3239

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

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@ import org.utbot.analytics.EngineAnalyticsContext
88
import org.utbot.analytics.FeatureProcessor
99
import org.utbot.analytics.Predictors
1010
import org.utbot.api.exception.UtMockAssumptionViolatedException
11-
import org.utbot.common.measureTime
1211
import org.utbot.common.debug
12+
import org.utbot.common.measureTime
13+
import org.utbot.common.tryLoadClass
1314
import org.utbot.engine.MockStrategy.NO_MOCKS
1415
import org.utbot.engine.pc.*
1516
import org.utbot.engine.selectors.*
@@ -32,17 +33,17 @@ import org.utbot.framework.UtSettings.pathSelectorStepsLimit
3233
import org.utbot.framework.UtSettings.pathSelectorType
3334
import org.utbot.framework.UtSettings.processUnknownStatesDuringConcreteExecution
3435
import org.utbot.framework.UtSettings.useDebugVisualization
35-
import org.utbot.framework.util.convertToAssemble
3636
import org.utbot.framework.plugin.api.*
3737
import org.utbot.framework.plugin.api.Step
3838
import org.utbot.framework.plugin.api.util.*
39+
import org.utbot.framework.util.convertToAssemble
3940
import org.utbot.framework.util.graph
4041
import org.utbot.framework.util.sootMethod
4142
import org.utbot.fuzzer.*
4243
import org.utbot.fuzzing.*
43-
import org.utbot.fuzzing.providers.ObjectValueProvider
4444
import org.utbot.fuzzing.providers.FieldValueProvider
45-
import org.utbot.fuzzing.spring.SavedEntityProvider
45+
import org.utbot.fuzzing.providers.ObjectValueProvider
46+
import org.utbot.fuzzing.spring.SavedEntityValueProvider
4647
import org.utbot.fuzzing.spring.SpringBeanValueProvider
4748
import org.utbot.fuzzing.utils.Trie
4849
import org.utbot.instrumentation.ConcreteExecutor
@@ -390,11 +391,22 @@ class UtBotSymbolicEngine(
390391
&& applicationContext.typeReplacementApproach is TypeReplacementApproach.ReplaceIfPossible
391392
) { provider ->
392393
val relevantRepositories = concreteExecutor.getRelevantSpringRepositories(methodUnderTest.classId)
393-
val generatedValueFieldIds = relevantRepositories.map {
394-
repository -> FieldId(repository.entityClassId, "id")
394+
logger.info { "Detected relevant repositories for class ${methodUnderTest.classId}: $relevantRepositories" }
395+
396+
val generatedValueAnnotationClasses = SpringModelUtils.generatedValueClassIds.mapNotNull {
397+
@Suppress("UNCHECKED_CAST") // type system fails to understand that GeneratedValue is indeed an annotation
398+
utContext.classLoader.tryLoadClass(it.name) as Class<out Annotation>?
395399
}
396400

397-
logger.info { "Detected relevant repositories for class ${methodUnderTest.classId}: $relevantRepositories" }
401+
val generatedValueFieldIds =
402+
relevantRepositories
403+
.map { it.entityClassId.jClass }
404+
.flatMap { entityClass -> generateSequence(entityClass) { it.superclass } }
405+
.flatMap { it.declaredFields.toList() }
406+
.filter { field -> generatedValueAnnotationClasses.any { field.isAnnotationPresent(it) } }
407+
.map { it.fieldId }
408+
logger.info { "Detected @GeneratedValue fields: $generatedValueFieldIds" }
409+
398410
// spring should try to generate bean values, but if it fails, then object value provider is used for it
399411
val springBeanValueProvider = SpringBeanValueProvider(
400412
defaultIdGenerator,
@@ -408,7 +420,7 @@ class UtBotSymbolicEngine(
408420
provider
409421
.except { p -> p is ObjectValueProvider }
410422
.with(springBeanValueProvider)
411-
.with(ValueProvider.of(relevantRepositories.map { SavedEntityProvider(defaultIdGenerator, it) }))
423+
.with(ValueProvider.of(relevantRepositories.map { SavedEntityValueProvider(defaultIdGenerator, it) }))
412424
.with(ValueProvider.of(generatedValueFieldIds.map { FieldValueProvider(defaultIdGenerator, it) }))
413425
}.let(transform)
414426
runJavaFuzzing(

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import org.utbot.framework.UtSettings.utBotGenerationTimeoutInMillis
2929
import org.utbot.framework.UtSettings.warmupConcreteExecution
3030
import org.utbot.framework.plugin.api.utils.checkFrameworkDependencies
3131
import org.utbot.framework.minimization.minimizeTestCase
32-
import org.utbot.framework.plugin.api.util.SpringModelUtils.entityClassId
32+
import org.utbot.framework.plugin.api.util.SpringModelUtils.entityClassIds
3333
import org.utbot.framework.plugin.api.util.id
3434
import org.utbot.framework.plugin.api.util.utContext
3535
import org.utbot.framework.plugin.services.JdkInfo
@@ -369,8 +369,8 @@ open class TestCaseGenerator(
369369
// in order to exclude such instructions for some reason
370370
// E.g. we exclude instructions (which classes have @Entity) when it is not a class under test
371371
// because we are not interested in coverage which was possibly produced by Spring itself
372-
val annotationsToIgnore =
373-
listOfNotNull(utContext.classLoader.tryLoadClass(entityClassId.name))
372+
val annotationsToIgnoreCoverage =
373+
entityClassIds.mapNotNull { utContext.classLoader.tryLoadClass(it.name) }
374374

375375
val buildDirsClassLoader = createBuildDirsClassLoader()
376376
val isClassOnUserClasspathCache = mutableMapOf<String, Boolean>()
@@ -401,7 +401,7 @@ open class TestCaseGenerator(
401401

402402
// We do not want to take instructions in classes
403403
// marked with annotations from [annotationsToIgnore]
404-
return@filter !hasAnnotations(instrClassFqn, annotationsToIgnore)
404+
return@filter !hasAnnotations(instrClassFqn, annotationsToIgnoreCoverage)
405405
}
406406
.ifEmpty {
407407
coverage.coveredInstructions

utbot-java-fuzzing/src/main/kotlin/org/utbot/fuzzing/spring/SavedEntity.kt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import org.utbot.framework.plugin.api.SpringRepositoryId
44
import org.utbot.framework.plugin.api.UtAssembleModel
55
import org.utbot.framework.plugin.api.UtReferenceModel
66
import org.utbot.framework.plugin.api.util.SpringModelUtils
7+
import org.utbot.framework.plugin.api.util.jClass
78
import org.utbot.fuzzer.FuzzedType
89
import org.utbot.fuzzer.FuzzedValue
910
import org.utbot.fuzzer.IdGenerator
@@ -13,17 +14,18 @@ import org.utbot.fuzzing.JavaValueProvider
1314
import org.utbot.fuzzing.Routine
1415
import org.utbot.fuzzing.Seed
1516
import org.utbot.fuzzing.providers.nullRoutine
17+
import org.utbot.fuzzing.toFuzzerType
1618

17-
class SavedEntityProvider(
19+
class SavedEntityValueProvider(
1820
private val idGenerator: IdGenerator<Int>,
1921
private val repositoryId: SpringRepositoryId
2022
) : JavaValueProvider {
21-
override fun accept(type: FuzzedType): Boolean = type.classId == repositoryId.entityClassId
23+
override fun accept(type: FuzzedType): Boolean = type.classId.jClass.isAssignableFrom(repositoryId.entityClassId.jClass)
2224

2325
override fun generate(description: FuzzedDescription, type: FuzzedType): Sequence<Seed<FuzzedType, FuzzedValue>> =
2426
sequenceOf(
2527
Seed.Recursive(
26-
construct = Routine.Create(listOf(type)) { values ->
28+
construct = Routine.Create(listOf(toFuzzerType(repositoryId.entityClassId.jClass, description.typeCache))) { values ->
2729
val entityValue = values.single()
2830
val entityModel = entityValue.model
2931
UtAssembleModel(

0 commit comments

Comments
 (0)