Skip to content

Commit 6d93b45

Browse files
committed
Make fuzzer use static methods to create arbitrary objects
1 parent ab0bdcb commit 6d93b45

File tree

2 files changed

+44
-12
lines changed

2 files changed

+44
-12
lines changed

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

+18-12
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,7 +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) }
167+
findAccessibleCreators(description, jClass.id).any()
168168
} catch (ignore: Throwable) {
169169
return false
170170
}
@@ -186,6 +186,12 @@ class AbstractsObjectValueProvider(
186186
}
187187
}
188188

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+
189195
internal class PublicSetterGetter(
190196
val setter: Method,
191197
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
@@ -301,6 +302,31 @@ class JavaFuzzingTest {
301302
seenStrings.forEach { assertInstanceOf(String::class.java, it) }
302303
}
303304

305+
@Test
306+
fun `fuzzer can create instances of classes without public constructors but with static factory method in their class`() {
307+
var seenLocalDateTime = false
308+
runBlockingWithContext {
309+
runJavaFuzzing(
310+
TestIdentityPreservingIdGenerator,
311+
methodUnderTest = LocalDateTime::getMinute.javaMethod!!.executableId,
312+
constants = emptyList(),
313+
names = emptyList(),
314+
) { thisInstance, _, _ ->
315+
val control = runCatching {
316+
ValueConstructor()
317+
.construct(listOfNotNull(thisInstance?.model))
318+
.singleOrNull()?.value
319+
}.getOrNull()?.let { constructedThisInstance ->
320+
assertInstanceOf(LocalDateTime::class.java, constructedThisInstance)
321+
seenLocalDateTime = true
322+
Control.STOP
323+
} ?: Control.CONTINUE
324+
BaseFeedback(Trie.emptyNode(), control)
325+
}
326+
}
327+
assertTrue(seenLocalDateTime) { "No value was generated for type LocalDateTime" }
328+
}
329+
304330
@Test
305331
fun `value providers override every function of fuzzing in simple case`() {
306332
val provided = MarkerValueProvider<FuzzedType, FuzzedValue, FuzzedDescription>("p")

0 commit comments

Comments
 (0)