Skip to content

Commit 670ab2e

Browse files
committed
Avoid harcoding the names of tables and repositories
1 parent a8672d0 commit 670ab2e

6 files changed

Lines changed: 87 additions & 26 deletions

File tree

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/builtin/SpringBuiltins.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ import org.utbot.framework.plugin.api.BuiltinClassId
55
internal val autowiredClassId = BuiltinClassId(
66
canonicalName = "org.springframework.beans.factory.annotation.Autowired",
77
simpleName = "Autowired",
8-
)
8+
)

utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/SpringUtExecutionInstrumentation.kt

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,13 @@ import org.utbot.instrumentation.instrumentation.ArgumentList
66
import org.utbot.instrumentation.instrumentation.Instrumentation
77
import org.utbot.instrumentation.instrumentation.execution.mock.SpringInstrumentationContext
88
import org.utbot.instrumentation.process.HandlerClassesLoader
9+
import org.utbot.instrumentation.util.AccessibleTypesAnalyzer
910
import org.utbot.spring.api.context.ContextWrapper
11+
import org.utbot.spring.api.context.NamedBean
1012
import org.utbot.spring.api.repositoryWrapper.RepositoryInteraction
13+
import java.lang.reflect.ParameterizedType
1114
import java.security.ProtectionDomain
15+
import java.util.IdentityHashMap
1216

1317
/**
1418
* UtExecutionInstrumentation wrapper that is aware of Spring config and initialises Spring context
@@ -20,6 +24,9 @@ class SpringUtExecutionInstrumentation(
2024
private lateinit var instrumentationContext: SpringInstrumentationContext
2125
private val springContext: ContextWrapper get() = instrumentationContext.springContext
2226

27+
private val typesAnalyzer = AccessibleTypesAnalyzer()
28+
private val relatedBeansCache = IdentityHashMap<Class<*>, Set<NamedBean>>()
29+
2330
companion object {
2431
private val logger = getLogger<SpringUtExecutionInstrumentation>()
2532
private const val SPRING_COMMONS_JAR_FILENAME = "utbot-spring-commons-shadow.jar"
@@ -43,22 +50,39 @@ class SpringUtExecutionInstrumentation(
4350
parameters: Any?
4451
): UtConcreteExecutionResult {
4552
RepositoryInteraction.recordedInteractions.clear()
46-
// TODO properly detect which beans need to be reset, right now "orderRepository" and "orderService" are hardcoded
47-
val beanNamesToReset = listOf("orderRepository", "orderService")
4853

49-
beanNamesToReset.forEach { beanNameToReset ->
50-
val beanDefToReset = springContext.getBeanDefinition(beanNameToReset)
51-
springContext.removeBeanDefinition(beanNameToReset)
52-
springContext.registerBeanDefinition(beanNameToReset, beanDefToReset)
54+
val beansToReset: Set<NamedBean> = relatedBeansCache.getOrPut(clazz) {
55+
val relatedBeans = springContext.getBeansOfType(clazz)
56+
val accessibleTypes = relatedBeans.flatMap { typesAnalyzer.collectAccessibleTypes(it.bean) }
57+
58+
val beansToReset = accessibleTypes
59+
.flatMap { springContext.getBeansOfType(it) }
60+
.toSet()
61+
62+
beansToReset
5363
}
5464

65+
beansToReset.forEach { springContext.resetBean(it.beanName) }
5566
val jdbcTemplate = getBean("jdbcTemplate")
56-
// TODO properly detect which repositories need to be cleared, right now "orders" is hardcoded
57-
val sql = "TRUNCATE TABLE orders"
67+
68+
val entityNames = beansToReset
69+
.filter {
70+
val beanType = it.bean::class.java
71+
val implementedInterfaces = beanType.genericInterfaces.map { i -> i.javaClass.canonicalName }
72+
"org.springframework.data.repository.Repository" in implementedInterfaces
73+
}
74+
.map { (it.bean::class.java.genericInterfaces[0] as ParameterizedType).actualTypeArguments[0] }
75+
.map { it.typeName }
76+
77+
//TODO: table name may be different from entity name (set with annotation on entity)
78+
//TODO: plural form is not always obtained with "+s", it can be even Person->People
79+
val tableNames = entityNames.map { it + "s" }
80+
81+
val sql = "TRUNCATE TABLE $tableNames"
5882
jdbcTemplate::class.java
5983
.getMethod("execute", sql::class.java)
6084
.invoke(jdbcTemplate, sql)
61-
val sql2 = "ALTER TABLE orders ALTER COLUMN id RESTART WITH 1"
85+
val sql2 = "ALTER TABLE $tableNames ALTER COLUMN id RESTART WITH 1"
6286
jdbcTemplate::class.java
6387
.getMethod("execute", sql::class.java)
6488
.invoke(jdbcTemplate, sql2)
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package org.utbot.instrumentation.util
2+
3+
import java.util.IdentityHashMap
4+
5+
class AccessibleTypesAnalyzer {
6+
fun collectAccessibleTypes(bean: Any): Set<Class<*>> {
7+
val accessibleObjects = collectFromObject(bean, analyzedObjects = IdentityHashMap())
8+
9+
return accessibleObjects
10+
.map { it::class.java }
11+
.toSet()
12+
}
13+
14+
private fun collectFromObject(obj: Any, analyzedObjects: IdentityHashMap<Any, Unit>): Set<Any> {
15+
if (analyzedObjects.contains(obj)) {
16+
return emptySet()
17+
}
18+
19+
analyzedObjects[obj] = Unit
20+
21+
val clazz = obj::class.java
22+
val objects = mutableSetOf<Any>()
23+
24+
var current: Class<*> = clazz
25+
while (current.superclass != null) {
26+
objects.addAll(current.fields.mapNotNull {
27+
it.get(obj)
28+
}.flatMap {
29+
listOf(it) + collectFromObject(it, analyzedObjects)
30+
})
31+
current = current.superclass
32+
}
33+
34+
return objects
35+
}
36+
}

utbot-spring-commons-api/src/main/kotlin/org/utbot/spring/api/context/ContextWrapper.kt

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,14 @@ package org.utbot.spring.api.context
33
interface ContextWrapper {
44
val context: Any
55

6-
fun getBeanDefinition(beanName: String): Any
6+
fun getBean(beanName: String): Any
77

8-
fun removeBeanDefinition(beanName: String)
8+
fun getBeansOfType(clazz: Class<*>): Set<NamedBean>
99

10-
fun registerBeanDefinition(beanName: String, beanDefinition: Any)
10+
fun resetBean(beanName: String): Any
11+
}
1112

12-
fun getBean(beanName: String): Any
13-
}
13+
data class NamedBean(
14+
val beanName: String,
15+
val bean: Any,
16+
)

utbot-spring-commons-api/src/main/kotlin/org/utbot/spring/api/repositoryWrapper/RepositoryInteraction.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@ data class RepositoryInteraction(
1111
companion object {
1212
val recordedInteractions = mutableListOf<RepositoryInteraction>()
1313
}
14-
}
14+
}
Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,21 @@
11
package org.utbot.spring.context
22

3-
import org.springframework.beans.factory.config.BeanDefinition
43
import org.springframework.beans.factory.support.BeanDefinitionRegistry
54
import org.springframework.context.ConfigurableApplicationContext
65
import org.utbot.spring.api.context.ContextWrapper
6+
import org.utbot.spring.api.context.NamedBean
77

88
class SpringContextWrapper(override val context: ConfigurableApplicationContext) : ContextWrapper {
9-
override fun getBeanDefinition(beanName: String): BeanDefinition = context.beanFactory.getBeanDefinition(beanName)
9+
override fun getBean(beanName: String): Any = context.getBean(beanName)
1010

11-
override fun removeBeanDefinition(beanName: String) {
12-
val beanDefinitionRegistry = context.beanFactory as BeanDefinitionRegistry
13-
beanDefinitionRegistry.removeBeanDefinition(beanName)
14-
}
11+
override fun getBeansOfType(clazz: Class<*>): Set<NamedBean> =
12+
context.beanFactory.getBeansOfType(clazz).map { NamedBean(it.key, it.value) }.toSet()
1513

16-
override fun registerBeanDefinition(beanName: String, beanDefinition: Any) {
14+
override fun resetBean(beanName: String) {
1715
val beanDefinitionRegistry = context.beanFactory as BeanDefinitionRegistry
18-
beanDefinition as BeanDefinition
16+
17+
val beanDefinition = context.beanFactory.getBeanDefinition(beanName)
18+
beanDefinitionRegistry.removeBeanDefinition(beanName)
1919
beanDefinitionRegistry.registerBeanDefinition(beanName, beanDefinition)
2020
}
21-
22-
override fun getBean(beanName: String): Any = context.getBean(beanName)
2321
}

0 commit comments

Comments
 (0)