Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make fuzzer use static methods to create arbitrary objects #2440

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.utbot.fuzzing.providers

import mu.KotlinLogging
import org.utbot.common.isStatic
import org.utbot.framework.UtSettings
import org.utbot.framework.plugin.api.*
import org.utbot.framework.plugin.api.util.*
Expand Down Expand Up @@ -46,33 +47,31 @@ class ObjectValueProvider(
description: FuzzedDescription,
type: FuzzedType
) = sequence {
val classId = type.classId
val constructors = classId.allConstructors
.filter {
isAccessible(it.constructor, description.description.packageName)
}
constructors.forEach { constructorId ->
yield(createValue(classId, constructorId, description))
findAccessibleCreators(description, type).forEach { creatorExecutableId ->
yield(createValue(type.classId, creatorExecutableId, description))
}
}

private fun createValue(classId: ClassId, constructorId: ConstructorId, description: FuzzedDescription): Seed.Recursive<FuzzedType, FuzzedValue> {
private fun createValue(classId: ClassId, creatorExecutableId: ExecutableId, description: FuzzedDescription): Seed.Recursive<FuzzedType, FuzzedValue> {
return Seed.Recursive(
construct = Routine.Create(constructorId.executable.genericParameterTypes.map {
construct = Routine.Create(creatorExecutableId.executable.genericParameterTypes.map {
toFuzzerType(it, description.typeCache)
}) { values ->
val id = idGenerator.createId()
UtAssembleModel(
id = id,
classId = classId,
modelName = "${constructorId.classId.name}${constructorId.parameters}#" + id.hex(),
modelName = "${creatorExecutableId.classId.name}.${creatorExecutableId.signature}#" + id.hex(),
instantiationCall = UtExecutableCallModel(
null,
constructorId,
creatorExecutableId,
values.map { it.model }),
modificationsChainProvider = { mutableListOf() }
).fuzzed {
summary = "%var% = ${classId.simpleName}(${constructorId.parameters.joinToString { it.simpleName }})"
summary = "%var% = ${when (creatorExecutableId) {
is ConstructorId -> classId.simpleName
is MethodId -> creatorExecutableId.simpleNameWithClass
}}(${creatorExecutableId.parameters.joinToString { it.simpleName }})"
}
},
modify = sequence {
Expand Down Expand Up @@ -164,12 +163,8 @@ class AbstractsObjectValueProvider(
}
val jClass = sc.id.jClass
return isAccessible(jClass, description.description.packageName) &&
jClass.declaredConstructors.any { isAccessible(it, description.description.packageName) } &&
jClass.let {
// This won't work in case of implementations with generics like `Impl<T> implements A<T>`.
// Should be reworked with accurate generic matching between all classes.
toFuzzerType(it, description.typeCache).traverseHierarchy(description.typeCache).contains(type)
}
findAccessibleCreators(description, toFuzzerType(jClass, description.typeCache)).any() &&
jClass.let { toFuzzerType(it, description.typeCache).isDefinitelySubtypeOf(description, type) }
} catch (ignore: Throwable) {
return false
}
Expand All @@ -191,6 +186,27 @@ class AbstractsObjectValueProvider(
}
}

private fun findAccessibleCreators(
description: FuzzedDescription,
neededType: FuzzedType
): Sequence<ExecutableId> =
(neededType.classId.allConstructors + (neededType.classId.jClass.methods
.filter {
it.isStatic && toFuzzerType(it.genericReturnType, description.typeCache)
.isDefinitelySubtypeOf(description, neededType)
}
.map { it.executableId })
).filter { isAccessible(it.executable, description.description.packageName) }

/**
* NOTE: this function takes conservative when generics are involved.
*
* For example, `false` may be returned when [this] is `List<T>` or `ArrayList<T>` while [other] is `List<String>`.
*/
// TODO should be reworked with accurate generic matching
private fun FuzzedType.isDefinitelySubtypeOf(description: FuzzedDescription, other: FuzzedType): Boolean =
classId.isSubtypeOf(other.classId) && traverseHierarchy(description.typeCache).contains(other)

internal class PublicSetterGetter(
val setter: Method,
val getter: Method,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import org.utbot.fuzzing.utils.Trie
import java.lang.reflect.GenericArrayType
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type
import java.time.LocalDateTime
import java.util.IdentityHashMap
import java.util.concurrent.atomic.AtomicInteger
import kotlin.reflect.jvm.javaMethod
Expand Down Expand Up @@ -177,6 +178,31 @@ class JavaFuzzingTest {
seenStrings.forEach { assertInstanceOf(String::class.java, it) }
}

@Test
fun `fuzzer can create instances of classes without public constructors but with static factory method in their class`() {
var seenLocalDateTime = false
runBlockingWithContext {
runJavaFuzzing(
TestIdentityPreservingIdGenerator,
methodUnderTest = LocalDateTime::getMinute.javaMethod!!.executableId,
constants = emptyList(),
names = emptyList(),
) { thisInstance, _, _ ->
val control = runCatching {
ValueConstructor()
.construct(listOfNotNull(thisInstance?.model))
.singleOrNull()?.value
}.getOrNull()?.let { constructedThisInstance ->
assertInstanceOf(LocalDateTime::class.java, constructedThisInstance)
seenLocalDateTime = true
Control.STOP
} ?: Control.CONTINUE
BaseFeedback(Trie.emptyNode(), control)
}
}
assertTrue(seenLocalDateTime) { "No value was generated for type LocalDateTime" }
}

@Test
fun `value providers override every function of fuzzing in simple case`() {
val provided = MarkerValueProvider<FuzzedType, FuzzedValue, FuzzedDescription>("p")
Expand Down
Loading