Skip to content

Commit 2d47527

Browse files
committed
Make fuzzer use static methods to create arbitrary objects
1 parent 339ef49 commit 2d47527

File tree

2 files changed

+44
-17
lines changed

2 files changed

+44
-17
lines changed

utbot-java-fuzzing/src/main/kotlin/org/utbot/fuzzing/providers/Objects.kt

+18-17
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.utbot.fuzzing.providers
22

33
import mu.KotlinLogging
4+
import org.utbot.common.isStatic
45
import org.utbot.framework.UtSettings
56
import org.utbot.framework.plugin.api.*
67
import org.utbot.framework.plugin.api.util.*
@@ -47,32 +48,31 @@ class ObjectValueProvider(
4748
type: FuzzedType
4849
) = sequence {
4950
val classId = type.classId
50-
val constructors = classId.allConstructors
51-
.filter {
52-
isAccessible(it.constructor, description.description.packageName)
53-
}
54-
constructors.forEach { constructorId ->
55-
yield(createValue(classId, constructorId, description))
51+
findAccessibleCreators(description, classId).forEach { creatorExecutableId ->
52+
yield(createValue(classId, creatorExecutableId, description))
5653
}
5754
}
5855

59-
private fun createValue(classId: ClassId, constructorId: ConstructorId, description: FuzzedDescription): Seed.Recursive<FuzzedType, FuzzedValue> {
56+
private fun createValue(classId: ClassId, creatorExecutableId: ExecutableId, description: FuzzedDescription): Seed.Recursive<FuzzedType, FuzzedValue> {
6057
return Seed.Recursive(
61-
construct = Routine.Create(constructorId.executable.genericParameterTypes.map {
58+
construct = Routine.Create(creatorExecutableId.executable.genericParameterTypes.map {
6259
toFuzzerType(it, description.typeCache)
6360
}) { values ->
6461
val id = idGenerator.createId()
6562
UtAssembleModel(
6663
id = id,
6764
classId = classId,
68-
modelName = "${constructorId.classId.name}${constructorId.parameters}#" + id.hex(),
65+
modelName = "${creatorExecutableId.classId.name}.${creatorExecutableId.signature}#" + id.hex(),
6966
instantiationCall = UtExecutableCallModel(
7067
null,
71-
constructorId,
68+
creatorExecutableId,
7269
values.map { it.model }),
7370
modificationsChainProvider = { mutableListOf() }
7471
).fuzzed {
75-
summary = "%var% = ${classId.simpleName}(${constructorId.parameters.joinToString { it.simpleName }})"
72+
summary = "%var% = ${when (creatorExecutableId) {
73+
is ConstructorId -> classId.simpleName
74+
is MethodId -> creatorExecutableId.simpleNameWithClass
75+
}}(${creatorExecutableId.parameters.joinToString { it.simpleName }})"
7676
}
7777
},
7878
modify = sequence {
@@ -164,12 +164,7 @@ class AbstractsObjectValueProvider(
164164
}
165165
val jClass = sc.id.jClass
166166
return isAccessible(jClass, description.description.packageName) &&
167-
jClass.declaredConstructors.any { isAccessible(it, description.description.packageName) } &&
168-
jClass.let {
169-
// This won't work in case of implementations with generics like `Impl<T> implements A<T>`.
170-
// Should be reworked with accurate generic matching between all classes.
171-
toFuzzerType(it, description.typeCache).traverseHierarchy(description.typeCache).contains(type)
172-
}
167+
findAccessibleCreators(description, jClass.id).any()
173168
} catch (ignore: Throwable) {
174169
return false
175170
}
@@ -191,6 +186,12 @@ class AbstractsObjectValueProvider(
191186
}
192187
}
193188

189+
private fun findAccessibleCreators(description: FuzzedDescription, classId: ClassId): Sequence<ExecutableId> =
190+
(classId.allConstructors + (classId.jClass.methods
191+
.filter { it.isStatic && it.returnType.id.isSubtypeOf(classId) }
192+
.map { it.executableId })
193+
).filter { isAccessible(it.executable, description.description.packageName) }
194+
194195
internal class PublicSetterGetter(
195196
val setter: Method,
196197
val getter: Method,

utbot-java-fuzzing/src/test/kotlin/org/utbot/fuzzing/JavaFuzzingTest.kt

+26
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import org.utbot.fuzzing.utils.Trie
2424
import java.lang.reflect.GenericArrayType
2525
import java.lang.reflect.ParameterizedType
2626
import java.lang.reflect.Type
27+
import java.time.LocalDateTime
2728
import java.util.IdentityHashMap
2829
import java.util.concurrent.atomic.AtomicInteger
2930
import kotlin.reflect.jvm.javaMethod
@@ -177,6 +178,31 @@ class JavaFuzzingTest {
177178
seenStrings.forEach { assertInstanceOf(String::class.java, it) }
178179
}
179180

181+
@Test
182+
fun `fuzzer can create instances of classes without public constructors but with static factory method in their class`() {
183+
var seenLocalDateTime = false
184+
runBlockingWithContext {
185+
runJavaFuzzing(
186+
TestIdentityPreservingIdGenerator,
187+
methodUnderTest = LocalDateTime::getMinute.javaMethod!!.executableId,
188+
constants = emptyList(),
189+
names = emptyList(),
190+
) { thisInstance, _, _ ->
191+
val control = runCatching {
192+
ValueConstructor()
193+
.construct(listOfNotNull(thisInstance?.model))
194+
.singleOrNull()?.value
195+
}.getOrNull()?.let { constructedThisInstance ->
196+
assertInstanceOf(LocalDateTime::class.java, constructedThisInstance)
197+
seenLocalDateTime = true
198+
Control.STOP
199+
} ?: Control.CONTINUE
200+
BaseFeedback(Trie.emptyNode(), control)
201+
}
202+
}
203+
assertTrue(seenLocalDateTime) { "No value was generated for type LocalDateTime" }
204+
}
205+
180206
@Test
181207
fun `value providers override every function of fuzzing in simple case`() {
182208
val provided = MarkerValueProvider<FuzzedType, FuzzedValue, FuzzedDescription>("p")

0 commit comments

Comments
 (0)