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

Better method mutations in Spring fuzzing #2541

Merged
merged 13 commits into from
Aug 24, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,11 @@ object UtSettings : AbstractSettings(logger, defaultKeyForSettingsPath, defaultS
*/
var fuzzingImplementationOfAbstractClasses: Boolean by getBooleanProperty(true)

/**
* Use methods to mutate fields of classes different from class under test or not.
*/
var tryMutateOtherClassesFieldsWithMethods: Boolean by getBooleanProperty(false)

/**
* Generate tests that treat possible overflows in arithmetic operations as errors
* that throw Arithmetic Exception.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.utbot.framework.plugin.services.JdkInfoDefaultProvider
import org.utbot.framework.util.SootUtils
import org.utbot.modifications.ModificationTransformationMode
import org.utbot.modifications.FieldInvolvementMode

internal class UtBotFieldModificatorsTest {
private lateinit var fieldsModificatorsSearcher: UtBotFieldsModificatorsSearcher
Expand Down Expand Up @@ -177,7 +177,7 @@ internal class UtBotFieldModificatorsTest {
jdkInfo = JdkInfoDefaultProvider().info
)
fieldsModificatorsSearcher = UtBotFieldsModificatorsSearcher(
modificationTransformationMode = ModificationTransformationMode.WriteOnly
fieldInvolvementMode = FieldInvolvementMode.WriteOnly
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import org.utbot.framework.plugin.services.JdkInfoDefaultProvider
import org.utbot.fuzzer.FuzzedType
import org.utbot.fuzzer.FuzzedValue
import org.utbot.fuzzing.FuzzedDescription
import org.utbot.fuzzing.JavaValueProvider
import org.utbot.fuzzing.Seed
import org.utbot.fuzzing.ValueProvider
import org.utbot.instrumentation.ConcreteExecutor
Expand Down Expand Up @@ -173,7 +174,7 @@ object UtBotJavaApi {
}
?.map { UtPrimitiveModel(it) } ?: emptySequence()

val customModelProvider = ValueProvider<FuzzedType, FuzzedValue, FuzzedDescription> { _, type ->
val customModelProvider = JavaValueProvider { _, type ->
sequence {
createPrimitiveModels(primitiveValuesSupplier, type.classId).forEach { model ->
yield(Seed.Simple(FuzzedValue(model)))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ import org.utbot.framework.plugin.api.util.jClass
import org.utbot.framework.util.nextModelName
import java.lang.reflect.Constructor
import java.util.IdentityHashMap
import org.utbot.modifications.ModificationTransformationMode
import org.utbot.modifications.FieldInvolvementMode

/**
* Creates [UtAssembleModel] from any [UtModel] or it's inner models if possible
Expand All @@ -75,7 +75,7 @@ class AssembleModelGenerator(private val basePackageName: String) {

private val modificatorsSearcher =
UtBotFieldsModificatorsSearcher(
modificationTransformationMode = ModificationTransformationMode.WriteOnly
fieldInvolvementMode = FieldInvolvementMode.WriteOnly
)
private val constructorAnalyzer = ConstructorAnalyzer()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ import org.utbot.framework.plugin.api.util.utContext
import org.utbot.fuzzer.IdentityPreservingIdGenerator
import org.utbot.fuzzing.JavaValueProvider
import org.utbot.fuzzing.ValueProvider
import org.utbot.fuzzing.providers.AbstractsObjectValueProvider
import org.utbot.fuzzing.providers.AnyDepthNullValueProvider
import org.utbot.fuzzing.providers.ModifyingWithMethodsProviderWrapper
import org.utbot.fuzzing.providers.ObjectValueProvider
import org.utbot.fuzzing.providers.anyObjectValueProvider
import org.utbot.fuzzing.spring.GeneratedFieldValueProvider
import org.utbot.fuzzing.spring.SpringBeanValueProvider
import org.utbot.fuzzing.spring.preserveProperties
Expand All @@ -37,28 +38,37 @@ class SpringIntegrationTestJavaFuzzingContext(
private val logger = KotlinLogging.logger {}
}

override val valueProvider: JavaValueProvider =
private val springBeanValueProvider: JavaValueProvider =
SpringBeanValueProvider(
idGenerator,
beanNameProvider = { classId ->
springApplicationContext.getBeansAssignableTo(classId).map { it.beanName }
},
relevantRepositories = relevantRepositories
)
.withFallback(ValidEntityValueProvider(idGenerator, onlyAcceptWhenValidIsRequired = true))

override val valueProvider: JavaValueProvider =
springBeanValueProvider.withModifyingMethodsBuddy()
.withFallback(ValidEntityValueProvider(idGenerator, onlyAcceptWhenValidIsRequired = true).withModifyingMethodsBuddy())
.withFallback(EmailValueProvider())
.withFallback(NotBlankStringValueProvider())
.withFallback(NotEmptyStringValueProvider())
.withFallback(
delegateContext.valueProvider
.except { p -> p is ObjectValueProvider }
.with(anyObjectValueProvider(idGenerator, shouldMutateWithMethods = true))
.with(ValidEntityValueProvider(idGenerator, onlyAcceptWhenValidIsRequired = false))
.with(ObjectValueProvider(idGenerator).withModifyingMethodsBuddy())
.with(ValidEntityValueProvider(idGenerator, onlyAcceptWhenValidIsRequired = false).withModifyingMethodsBuddy())
.with(createGeneratedFieldValueProviders(relevantRepositories, idGenerator))
.withFallback(AnyDepthNullValueProvider)
)
.preserveProperties()

private fun JavaValueProvider.withModifyingMethodsBuddy(): JavaValueProvider =
with(modifyingMethodsBuddy(this))

private fun modifyingMethodsBuddy(provider: JavaValueProvider): JavaValueProvider =
ModifyingWithMethodsProviderWrapper(classUnderTest, provider)


private fun createGeneratedFieldValueProviders(
relevantRepositories: Set<SpringRepositoryId>,
idGenerator: IdentityPreservingIdGenerator<Int>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ fun defaultValueProviders(idGenerator: IdentityPreservingIdGenerator<Int>) = lis
FloatValueProvider,
StringValueProvider,
NumberValueProvider,
anyObjectValueProvider(idGenerator, shouldMutateWithMethods = false),
anyObjectValueProvider(idGenerator),
ArrayValueProvider(idGenerator),
EnumValueProvider(idGenerator),
ListSetValueProvider(idGenerator),
Expand All @@ -59,7 +59,7 @@ suspend fun runJavaFuzzing(
methodUnderTest: ExecutableId,
constants: Collection<FuzzedConcreteValue>,
names: List<String>,
providers: List<ValueProvider<FuzzedType, FuzzedValue, FuzzedDescription>> = defaultValueProviders(idGenerator),
providers: List<JavaValueProvider> = defaultValueProviders(idGenerator),
exec: suspend (thisInstance: FuzzedValue?, description: FuzzedDescription, values: List<FuzzedValue>) -> BaseFeedback<Trie.Node<Instruction>, FuzzedType, FuzzedValue>
) {
val random = Random(0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import org.utbot.fuzzing.*

class ArrayValueProvider(
val idGenerator: IdGenerator<Int>,
) : ValueProvider<FuzzedType, FuzzedValue, FuzzedDescription> {
) : JavaValueProvider {

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import kotlin.reflect.KClass

class EmptyCollectionValueProvider(
val idGenerator: IdGenerator<Int>
) : ValueProvider<FuzzedType, FuzzedValue, FuzzedDescription> {
) : JavaValueProvider {
private class Info(val classId: ClassId, val methodName: String, val returnType: ClassId = classId)

private val unmodifiableCollections = listOf(
Expand Down Expand Up @@ -150,7 +150,7 @@ class ListSetValueProvider(
abstract class CollectionValueProvider(
private val idGenerator: IdGenerator<Int>,
vararg acceptableCollectionTypes: ClassId
) : ValueProvider<FuzzedType, FuzzedValue, FuzzedDescription> {
) : JavaValueProvider {

private val acceptableCollectionTypes = acceptableCollectionTypes.toList()

Expand Down Expand Up @@ -216,7 +216,7 @@ abstract class CollectionValueProvider(
}
}

class IteratorValueProvider(val idGenerator: IdGenerator<Int>) : ValueProvider<FuzzedType, FuzzedValue, FuzzedDescription> {
class IteratorValueProvider(val idGenerator: IdGenerator<Int>) : JavaValueProvider {
override fun accept(type: FuzzedType): Boolean {
return type.classId == Iterator::class.id
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ import org.utbot.fuzzer.FuzzedValue
import org.utbot.fuzzer.IdentityPreservingIdGenerator
import org.utbot.fuzzer.fuzzed
import org.utbot.fuzzing.FuzzedDescription
import org.utbot.fuzzing.JavaValueProvider
import org.utbot.fuzzing.Seed
import org.utbot.fuzzing.ValueProvider

class EnumValueProvider(
val idGenerator: IdentityPreservingIdGenerator<Int>,
) : ValueProvider<FuzzedType, FuzzedValue, FuzzedDescription> {
) : JavaValueProvider {
override fun accept(type: FuzzedType) = type.classId.isEnum

override fun generate(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package org.utbot.fuzzing.providers

import org.utbot.framework.plugin.api.ClassId
import org.utbot.framework.plugin.api.UtAssembleModel
import org.utbot.framework.plugin.api.UtExecutableCallModel
import org.utbot.framework.plugin.api.util.executableId
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

/**
* Value provider that is a buddy for another provider
* that keeps all it's functionality and also allows
* to use methods to mutate field states of an object.
*
* NOTE!!!
* Instances represented by [UtAssembleModel] only can be mutated with methods.
*/
class ModifyingWithMethodsProviderWrapper(
private val classUnderTest: ClassId,
private val delegate: JavaValueProvider
) : JavaValueProvider by delegate {

override fun generate(description: FuzzedDescription, type: FuzzedType): Sequence<Seed<FuzzedType, FuzzedValue>> =
delegate
.generate(description, type)
.map { seed ->
if (seed is Seed.Recursive) {
Seed.Recursive(
construct = seed.construct,
modify = seed.modify +
findMethodsToModifyWith(description, type.classId, classUnderTest)
.asSequence()
.map { md ->
Routine.Call(md.parameterTypes) { self, values ->
val model = self.model as UtAssembleModel
model.modificationsChain as MutableList +=
UtExecutableCallModel(
model,
md.method.executableId,
values.map { it.model }
)
}
},
empty = seed.empty,
)
} 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)
}
Loading