Skip to content

Enhance fuzzing for arrays #738 #755

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

Merged
merged 5 commits into from
Aug 30, 2022
Merged
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
Expand Up @@ -439,7 +439,7 @@ class UtBotSymbolicEngine(
packageName = executableId.classId.packageName
}
fuzz(thisMethodDescription, ObjectModelProvider(defaultIdGenerator).apply {
limitValuesCreatedByFieldAccessors = 500
totalLimit = 500
})
}.withMutations(
TrieBasedFuzzerStatistics(coveredInstructionValues), methodUnderTestDescription, *defaultModelMutators().toTypedArray()
Expand Down
16 changes: 11 additions & 5 deletions utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/Fuzzer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -164,22 +164,28 @@ fun defaultModelProviders(idGenerator: IdentityPreservingIdGenerator<Int>): Mode
}

/**
* Creates a model provider for [ObjectModelProvider] that generates values for object constructor.
* Creates a model provider from a list of providers that we want to use by default in [RecursiveModelProvider]
*/
fun objectModelProviders(idGenerator: IdentityPreservingIdGenerator<Int>): ModelProvider {
return ModelProvider.of(
internal fun modelProviderForRecursiveCalls(idGenerator: IdentityPreservingIdGenerator<Int>, recursionDepth: Int): ModelProvider {
val nonRecursiveProviders = ModelProvider.of(
CollectionModelProvider(idGenerator),
ArrayModelProvider(idGenerator),
EnumModelProvider(idGenerator),
StringConstantModelProvider,
RegexModelProvider,
CharToStringModelProvider,
ConstantsModelProvider,
PrimitiveDefaultsModelProvider,
PrimitiveWrapperModelProvider,
)

return if (recursionDepth >= 0)
nonRecursiveProviders
.with(ObjectModelProvider(idGenerator, recursionDepth))
.with(ArrayModelProvider(idGenerator, recursionDepth))
else
nonRecursiveProviders
}


fun defaultModelMutators(): List<ModelMutator> = listOf(
StringRandomMutator,
RegexStringModelMutator,
Expand Down
25 changes: 24 additions & 1 deletion utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/ModelProvider.kt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,17 @@ fun interface ModelProvider {
}
}

/**
* Applies [transform] for current provider
*/
fun map(transform: (ModelProvider) -> ModelProvider): ModelProvider {
return if (this is Combined) {
Combined(providers.map(transform))
} else {
transform(this)
}
}

/**
* Creates [ModelProvider] that passes unprocessed classes to `modelProvider`.
*
Expand Down Expand Up @@ -123,7 +134,19 @@ fun interface ModelProvider {
/**
* Wrapper class that delegates implementation to the [providers].
*/
private class Combined(val providers: List<ModelProvider>): ModelProvider {
private class Combined(providers: List<ModelProvider>): ModelProvider {
val providers: List<ModelProvider>

init {
// Flattening to avoid Combined inside Combined (for correct work of except, map, etc.)
this.providers = providers.flatMap {
if (it is Combined)
it.providers
else
listOf(it)
}
}

override fun generate(description: FuzzedMethodDescription): Sequence<FuzzedParameter> = sequence {
providers.forEach { provider ->
provider.generate(description).forEach {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,34 +1,65 @@
package org.utbot.fuzzer.providers

import org.utbot.framework.plugin.api.ClassId
import org.utbot.framework.plugin.api.UtArrayModel
import org.utbot.framework.plugin.api.UtModel
import org.utbot.framework.plugin.api.UtPrimitiveModel
import org.utbot.framework.plugin.api.util.defaultValueModel
import org.utbot.framework.plugin.api.util.intClassId
import org.utbot.framework.plugin.api.util.isArray
import org.utbot.fuzzer.FuzzedMethodDescription
import org.utbot.fuzzer.FuzzedParameter
import org.utbot.fuzzer.IdGenerator
import org.utbot.fuzzer.ModelProvider
import org.utbot.fuzzer.ModelProvider.Companion.yieldAllValues
import java.util.function.IntSupplier
import org.utbot.fuzzer.IdentityPreservingIdGenerator

class ArrayModelProvider(
private val idGenerator: IdGenerator<Int>
) : ModelProvider {
override fun generate(description: FuzzedMethodDescription): Sequence<FuzzedParameter> = sequence {
description.parametersMap
.asSequence()
.filter { (classId, _) -> classId.isArray }
.forEach { (arrayClassId, indices) ->
yieldAllValues(indices, listOf(0, 10).map { arraySize ->
UtArrayModel(
id = idGenerator.createId(),
arrayClassId,
length = arraySize,
arrayClassId.elementClassId!!.defaultValueModel(),
mutableMapOf()
).fuzzed {
this.summary = "%var% = ${arrayClassId.elementClassId!!.simpleName}[$arraySize]"
}
})
idGenerator: IdentityPreservingIdGenerator<Int>,
recursionDepthLeft: Int = 1
) : RecursiveModelProvider(idGenerator, recursionDepthLeft) {
override fun createNewInstance(parentProvider: RecursiveModelProvider, newTotalLimit: Int): RecursiveModelProvider =
ArrayModelProvider(parentProvider.idGenerator, parentProvider.recursionDepthLeft - 1)
.copySettingsFrom(parentProvider)
.apply {
totalLimit = newTotalLimit
if (parentProvider is ArrayModelProvider)
branchingLimit = 1 // This improves performance but also forbids generating "jagged" arrays
}

override fun generateModelConstructors(
description: FuzzedMethodDescription,
classId: ClassId
): List<ModelConstructor> {
if (!classId.isArray)
return listOf()
val lengths = generateArrayLengths(description)
return lengths.map { length ->
ModelConstructor(List(length) { classId.elementClassId!! }) { values ->
createFuzzedArrayModel(classId, length, values.map { it.model } )
}
}
}

private fun generateArrayLengths(description: FuzzedMethodDescription): List<Int> {
val fuzzedLengths = fuzzValuesRecursively(
types = listOf(intClassId),
baseMethodDescription = description,
modelProvider = ConstantsModelProvider
)

// Firstly we will use most "interesting" default sizes, then we will use small sizes obtained from src
return listOf(3, 0) + fuzzedLengths
.map { (it.single().model as UtPrimitiveModel).value as Int }
.filter { it in 1..10 && it != 3 }
.toSortedSet()
.toList()
}

private fun createFuzzedArrayModel(arrayClassId: ClassId, length: Int, values: List<UtModel>?) =
UtArrayModel(
idGenerator.createId(),
arrayClassId,
length,
arrayClassId.elementClassId!!.defaultValueModel(),
values?.withIndex()?.associate { it.index to it.value }?.toMutableMap() ?: mutableMapOf()
).fuzzed {
this.summary = "%var% = ${arrayClassId.elementClassId!!.simpleName}[$length]"
}
}
Loading