Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -1397,7 +1397,7 @@ class Traverser(
// from Spring bean definitions, for example), we can just create a symbolic object
// with hard constraint on the mentioned type.
val replacedClassId = when (typeReplacer.typeReplacementMode) {
KnownImplementor -> typeReplacer.replaceTypeIfNeeded(type)
KnownImplementor -> typeReplacer.replaceTypeIfNeeded(type.id)
AnyImplementor,
NoImplementors -> null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,6 @@ class UtBotSymbolicEngine(
* Run fuzzing flow.
*
* @param until is used by fuzzer to cancel all tasks if the current time is over this value
* @param transform provides model values for a method
*/
fun fuzzing(until: Long = Long.MAX_VALUE) = flow {
val isFuzzable = methodUnderTest.parameters.all { classId ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package org.utbot.framework.context

import org.utbot.framework.plugin.api.ClassId
import org.utbot.framework.plugin.api.TypeReplacementMode
import soot.RefType

interface TypeReplacer {
/**
Expand All @@ -14,5 +13,5 @@ interface TypeReplacer {
* Finds a type to replace the original abstract type
* if it is guided with some additional information.
*/
fun replaceTypeIfNeeded(type: RefType): ClassId?
fun replaceTypeIfNeeded(classId: ClassId): ClassId?
}
Original file line number Diff line number Diff line change
@@ -1,41 +1,51 @@
package org.utbot.framework.context.custom

import org.utbot.framework.context.JavaFuzzingContext
import org.utbot.framework.plugin.api.UtNullModel
import org.utbot.framework.plugin.api.ExecutableId
import org.utbot.fuzzer.FuzzedType
import org.utbot.fuzzing.JavaValueProvider
import org.utbot.fuzzing.providers.MapValueProvider
import org.utbot.fuzzing.Seed
import org.utbot.fuzzing.providers.AnyDepthNullValueProvider
import org.utbot.fuzzing.providers.AnyObjectValueProvider
import org.utbot.fuzzing.spring.unit.MockValueProvider
import org.utbot.fuzzing.providers.NullValueProvider
import org.utbot.fuzzing.providers.ObjectValueProvider
import org.utbot.fuzzing.providers.StringValueProvider
import org.utbot.fuzzing.spring.decorators.filterSeeds
import org.utbot.fuzzing.spring.decorators.filterTypes
import org.utbot.instrumentation.instrumentation.execution.UtConcreteExecutionResult

/**
* Makes fuzzer mock all types that don't have *specific* [JavaValueProvider],
* like [MapValueProvider] or [StringValueProvider].
* Makes fuzzer to use mocks in accordance with [mockPredicate].
*
* NOTE: the caller is responsible for providing some *specific* [JavaValueProvider]
* that can create values for class under test (otherwise it will be mocked),
* [ObjectValueProvider] and [NullValueProvider] do not count as *specific*.
* NOTE:
* - fuzzer won't mock types, that have *specific* value providers
* (i.e. ones that do not implement [AnyObjectValueProvider])
* - fuzzer may still resort to mocks despite [mockPredicate] and *specific*
* value providers if it can't create other non-null values or at runtime
*/
fun JavaFuzzingContext.mockAllTypesWithoutSpecificValueProvider() =
MockingJavaFuzzingContext(delegateContext = this)
fun JavaFuzzingContext.useMocks(mockPredicate: (FuzzedType) -> Boolean) =
MockingJavaFuzzingContext(delegateContext = this, mockPredicate)

class MockingJavaFuzzingContext(
val delegateContext: JavaFuzzingContext
val delegateContext: JavaFuzzingContext,
val mockPredicate: (FuzzedType) -> Boolean,
) : JavaFuzzingContext by delegateContext {
private val mockValueProvider = MockValueProvider(delegateContext.idGenerator)

override val valueProvider: JavaValueProvider =
// NOTE: we first remove `NullValueProvider` from `delegateContext.valueProvider` and then
// add it back as a part of our `withFallback` so it has the same priority as
// `mockValueProvider`, otherwise mocks will never be used where `null` can be used.

delegateContext.valueProvider
.except { it is NullValueProvider }
.except { it is ObjectValueProvider }
// NOTE: we first remove `AnyObjectValueProvider` and `NullValueProvider` from `delegateContext.valueProvider`
// and then add them back as a part of our `withFallback` so they have the same priority as
// `mockValueProvider`, otherwise mocks will never be used where `null` or new object can be used.
.except { it is AnyObjectValueProvider }
.withFallback(
mockValueProvider
.with(NullValueProvider)
mockValueProvider.filterTypes(mockPredicate)
.with(
delegateContext.valueProvider
.filterTypes { !mockPredicate(it) }
.filterSeeds { (it as? Seed.Simple)?.value?.model !is UtNullModel }
)
.withFallback(mockValueProvider.with(AnyDepthNullValueProvider))
)

override fun handleFuzzedConcreteExecutionResult(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@ package org.utbot.framework.context.simple
import org.utbot.framework.context.TypeReplacer
import org.utbot.framework.plugin.api.ClassId
import org.utbot.framework.plugin.api.TypeReplacementMode
import soot.RefType

class SimpleTypeReplacer : TypeReplacer {
override val typeReplacementMode: TypeReplacementMode = TypeReplacementMode.AnyImplementor

override fun replaceTypeIfNeeded(type: RefType): ClassId? = null
override fun replaceTypeIfNeeded(classId: ClassId): ClassId? = null
}
25 changes: 10 additions & 15 deletions utbot-fuzzing/src/main/kotlin/org/utbot/fuzzing/Providers.kt
Original file line number Diff line number Diff line change
Expand Up @@ -92,26 +92,16 @@ fun interface ValueProvider<T, R, D : Description<T>> {
}

/**
* Removes `anotherValueProviders<T, R` from current one.
* Removes providers matching [filter] from the current one.
*/
fun except(filter: (ValueProvider<T, R, D>) -> Boolean): ValueProvider<T, R, D> {
return if (this is Combined) {
Combined(providers.map { it.unwrapIfFallback() }.filterNot(filter))
} else {
Combined(if (filter(unwrapIfFallback())) emptyList() else listOf(this))
}
}
fun except(filter: (ValueProvider<T, R, D>) -> Boolean): ValueProvider<T, R, D> =
map { if (filter(it)) Combined(emptyList()) else it }

/**
* Applies [transform] for current provider
*/
fun map(transform: (ValueProvider<T, R, D>) -> ValueProvider<T, R, D>): ValueProvider<T, R, D> {
return if (this is Combined) {
Combined(providers.map(transform))
} else {
transform(this)
}
}
fun map(transform: (ValueProvider<T, R, D>) -> ValueProvider<T, R, D>): ValueProvider<T, R, D> =
transform(this)

/**
* Uses fallback value provider in case when 'this' one failed to generate any value.
Expand Down Expand Up @@ -167,6 +157,8 @@ fun interface ValueProvider<T, R, D : Description<T>> {
}
}

override fun map(transform: (ValueProvider<T, R, D>) -> ValueProvider<T, R, D>): ValueProvider<T, R, D> =
transform(Fallback(provider.map(transform), fallback.map(transform)))
}

/**
Expand Down Expand Up @@ -200,6 +192,9 @@ fun interface ValueProvider<T, R, D : Description<T>> {
}
}
}

override fun map(transform: (ValueProvider<T, R, D>) -> ValueProvider<T, R, D>): ValueProvider<T, R, D> =
transform(Combined(providers.map { it.map(transform) }))
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ internal fun FuzzedType.traverseHierarchy(typeCache: MutableMap<Type, FuzzedType
* @param type to be resolved
* @param cache is used to store same [FuzzedType] for same java types
*/
internal fun toFuzzerType(type: Type, cache: MutableMap<Type, FuzzedType>): FuzzedType {
fun toFuzzerType(type: Type, cache: MutableMap<Type, FuzzedType>): FuzzedType {
return toFuzzerType(
type = type,
classId = { t -> toClassId(t, cache) },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,16 @@ fun anyObjectValueProvider(idGenerator: IdentityPreservingIdGenerator<Int>) =
ovp.withFallback(AbstractsObjectValueProvider(idGenerator))
}

/**
* Marker interface that shows that this [JavaValueProvider] can potentially provide values of
* arbitrary types, unlike type-specific value providers that were designed to provide values of
* few specific popular types (e.g. `List`, `String`, etc.).
*/
interface AnyObjectValueProvider

class ObjectValueProvider(
val idGenerator: IdGenerator<Int>,
) : JavaValueProvider {
) : JavaValueProvider, AnyObjectValueProvider {

override fun accept(type: FuzzedType) = !isIgnored(type.classId)

Expand Down Expand Up @@ -140,7 +147,7 @@ class ObjectValueProvider(
}

@Suppress("unused")
object NullValueProvider : JavaValueProvider {
object NullValueProvider : JavaValueProvider, AnyObjectValueProvider {

override fun enrich(description: FuzzedDescription, type: FuzzedType, scope: Scope) {
// any value in static function is ok to fuzz
Expand Down Expand Up @@ -170,7 +177,7 @@ object NullValueProvider : JavaValueProvider {
*
* Intended to be used as a last fallback.
*/
object AnyDepthNullValueProvider : JavaValueProvider {
object AnyDepthNullValueProvider : JavaValueProvider, AnyObjectValueProvider {

override fun accept(type: FuzzedType) = type.classId.isRefType

Expand All @@ -185,7 +192,7 @@ object AnyDepthNullValueProvider : JavaValueProvider {
*/
class AbstractsObjectValueProvider(
val idGenerator: IdGenerator<Int>,
) : JavaValueProvider {
) : JavaValueProvider, AnyObjectValueProvider {

override fun accept(type: FuzzedType) = type.classId.isRefType && !isKnownTypes(type.classId)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.utbot.fuzzing.providers
package org.utbot.fuzzing.spring.decorators

import org.utbot.framework.plugin.api.ClassId
import org.utbot.framework.plugin.api.UtAssembleModel
Expand All @@ -9,8 +9,8 @@ import org.utbot.fuzzer.FuzzedValue
import org.utbot.fuzzing.FuzzedDescription
import org.utbot.fuzzing.JavaValueProvider
import org.utbot.fuzzing.Routine
import org.utbot.fuzzing.Scope
import org.utbot.fuzzing.Seed
import org.utbot.fuzzing.providers.findMethodsToModifyWith

/**
* Value provider that is a buddy for another provider
Expand All @@ -22,8 +22,11 @@ import org.utbot.fuzzing.Seed
*/
class ModifyingWithMethodsProviderWrapper(
private val classUnderTest: ClassId,
private val delegate: JavaValueProvider
) : JavaValueProvider by delegate {
delegate: JavaValueProvider
) : ValueProviderDecorator<FuzzedType, FuzzedValue, FuzzedDescription>(delegate) {

override fun wrap(provider: JavaValueProvider): JavaValueProvider =
ModifyingWithMethodsProviderWrapper(classUnderTest, provider)

override fun generate(description: FuzzedDescription, type: FuzzedType): Sequence<Seed<FuzzedType, FuzzedValue>> =
delegate
Expand All @@ -50,9 +53,4 @@ class ModifyingWithMethodsProviderWrapper(
)
} else seed
}

override fun enrich(description: FuzzedDescription, type: FuzzedType, scope: Scope) =
delegate.enrich(description, type, scope)

override fun accept(type: FuzzedType): Boolean = delegate.accept(type)
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package org.utbot.fuzzing.spring
package org.utbot.fuzzing.spring.decorators

import org.utbot.common.toDynamicProperties
import org.utbot.fuzzer.FuzzedType
import org.utbot.fuzzer.FuzzedValue
import org.utbot.fuzzing.FuzzedDescription
import org.utbot.fuzzing.JavaValueProvider
import org.utbot.fuzzing.Routine
import org.utbot.fuzzing.Scope
import org.utbot.fuzzing.Seed
import org.utbot.fuzzing.spring.FuzzedTypeProperty
import org.utbot.fuzzing.spring.addProperties
import org.utbot.fuzzing.spring.properties

/**
* @see preserveProperties
Expand All @@ -25,14 +27,14 @@ interface PreservableFuzzedTypeProperty<T> : FuzzedTypeProperty<T>
fun JavaValueProvider.preserveProperties() : JavaValueProvider =
PropertyPreservingValueProvider(this)

class PropertyPreservingValueProvider(private val delegateProvider: JavaValueProvider) : JavaValueProvider {
override fun enrich(description: FuzzedDescription, type: FuzzedType, scope: Scope) =
delegateProvider.enrich(description, type, scope)

override fun accept(type: FuzzedType): Boolean = delegateProvider.accept(type)
class PropertyPreservingValueProvider(
delegate: JavaValueProvider
) : ValueProviderDecorator<FuzzedType, FuzzedValue, FuzzedDescription>(delegate) {
override fun wrap(provider: JavaValueProvider): JavaValueProvider =
provider.preserveProperties()

override fun generate(description: FuzzedDescription, type: FuzzedType): Sequence<Seed<FuzzedType, FuzzedValue>> {
val delegateSeeds = delegateProvider.generate(description, type)
val delegateSeeds = delegate.generate(description, type)

val preservedProperties = type.properties.entries
.filter { it.property is PreservableFuzzedTypeProperty }
Expand Down Expand Up @@ -67,10 +69,4 @@ class PropertyPreservingValueProvider(private val delegateProvider: JavaValuePro
}
}
}

override fun map(transform: (JavaValueProvider) -> JavaValueProvider): JavaValueProvider =
delegateProvider.map(transform).preserveProperties()

override fun except(filter: (JavaValueProvider) -> Boolean): JavaValueProvider =
delegateProvider.except(filter).preserveProperties()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.utbot.fuzzing.spring.decorators

import org.utbot.fuzzing.Description
import org.utbot.fuzzing.Seed
import org.utbot.fuzzing.ValueProvider

fun <T, R, D : Description<T>> ValueProvider<T, R, D>.filterSeeds(predicate: (Seed<T, R>) -> Boolean) =
SeedFilteringValueProvider(delegate = this, predicate)

class SeedFilteringValueProvider<T, R, D : Description<T>>(
delegate: ValueProvider<T, R, D>,
private val predicate: (Seed<T, R>) -> Boolean
) : ValueProviderDecorator<T, R, D>(delegate) {
override fun wrap(provider: ValueProvider<T, R, D>): ValueProvider<T, R, D> =
provider.filterSeeds(predicate)

override fun generate(description: D, type: T): Sequence<Seed<T, R>> =
delegate.generate(description, type).filter(predicate)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package org.utbot.fuzzing.spring.decorators

import org.utbot.fuzzing.Description
import org.utbot.fuzzing.ValueProvider

fun <T, R, D : Description<T>> ValueProvider<T, R, D>.filterTypes(predicate: (T) -> Boolean) =
TypeFilteringValueProvider(delegate = this, predicate)

class TypeFilteringValueProvider<T, R, D : Description<T>>(
delegate: ValueProvider<T, R, D>,
private val predicate: (T) -> Boolean
) : ValueProviderDecorator<T, R, D>(delegate) {
override fun wrap(provider: ValueProvider<T, R, D>): ValueProvider<T, R, D> =
provider.filterTypes(predicate)

override fun accept(type: T): Boolean =
predicate(type) && super.accept(type)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.utbot.fuzzing.spring.decorators

import org.utbot.fuzzing.Description
import org.utbot.fuzzing.Scope
import org.utbot.fuzzing.Seed
import org.utbot.fuzzing.ValueProvider

fun <T, R, D : Description<T>> ValueProvider<T, R, D>.replaceTypes(typeReplacer: (D, T) -> T) =
TypeReplacingValueProvider(delegate = this, typeReplacer)

class TypeReplacingValueProvider<T, R, D : Description<T>>(
delegate: ValueProvider<T, R, D>,
private val typeReplacer: (D, T) -> T
) : ValueProviderDecorator<T, R, D>(delegate) {
override fun wrap(provider: ValueProvider<T, R, D>): ValueProvider<T, R, D> =
provider.replaceTypes(typeReplacer)

override fun enrich(description: D, type: T, scope: Scope) =
super.enrich(description, typeReplacer(description, type), scope)

override fun accept(type: T): Boolean = true

override fun generate(description: D, type: T): Sequence<Seed<T, R>> =
if (super.accept(typeReplacer(description, type)))
super.generate(description, typeReplacer(description, type))
else
emptySequence()
}
Loading