Skip to content

Commit

Permalink
Better method mutations in Spring fuzzing #2502 (#2541)
Browse files Browse the repository at this point in the history
  • Loading branch information
EgorkaKulikov authored Aug 24, 2023
1 parent abb6268 commit 0587302
Show file tree
Hide file tree
Showing 19 changed files with 250 additions and 149 deletions.
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

0 comments on commit 0587302

Please sign in to comment.