Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

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 @@ -32,7 +32,7 @@ class FuzzedMethodDescription(
}

constructor(executableId: ExecutableId, concreteValues: Collection<FuzzedConcreteValue> = emptyList()) : this(
executableId.name,
executableId.classId.simpleName + "." + executableId.name,
executableId.returnType,
executableId.parameters,
concreteValues
Expand Down
54 changes: 42 additions & 12 deletions utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/Fuzzer.kt
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
package org.utbot.fuzzer

import org.utbot.framework.plugin.api.ClassId
import org.utbot.framework.plugin.api.UtModel
import org.utbot.framework.plugin.api.util.defaultValueModel
import org.utbot.fuzzer.providers.ConstantsModelProvider
import org.utbot.fuzzer.providers.ObjectModelProvider
import org.utbot.fuzzer.providers.PrimitivesModelProvider
import org.utbot.fuzzer.providers.StringConstantModelProvider
import mu.KotlinLogging
import org.utbot.fuzzer.providers.ArrayModelProvider
import org.utbot.fuzzer.providers.CharToStringModelProvider
import org.utbot.fuzzer.providers.CollectionModelProvider
import org.utbot.fuzzer.providers.PrimitiveDefaultsModelProvider
import org.utbot.fuzzer.providers.EnumModelProvider
import org.utbot.fuzzer.providers.PrimitiveWrapperModelProvider
import java.util.concurrent.atomic.AtomicInteger
import java.util.function.ToIntFunction
import java.util.function.IntSupplier
import kotlin.random.Random

private val logger = KotlinLogging.logger {}
Expand All @@ -24,21 +28,47 @@ fun fuzz(description: FuzzedMethodDescription, vararg modelProviders: ModelProvi
description.parameters.forEachIndexed { index, classId ->
val models = values[index]
if (models.isEmpty()) {
logger.warn { "There's no models provided classId=$classId. Null or default value will be provided" }
models.add(classId.defaultValueModel())
logger.warn { "There's no models provided classId=$classId. No suitable values are generated for ${description.name}" }
return emptySequence()
}
}
return CartesianProduct(values, Random(0L)).asSequence()
}

fun defaultModelProviders(idGenerator: ToIntFunction<ClassId> = SimpleIdGenerator()): ModelProvider {
return ObjectModelProvider(idGenerator)
.with(ConstantsModelProvider)
.with(StringConstantModelProvider)
.with(PrimitivesModelProvider)
/**
* Creates a model provider from a list of default providers.
*/
fun defaultModelProviders(idGenerator: IntSupplier = SimpleIdGenerator()): ModelProvider {
return ModelProvider.of(
ObjectModelProvider(idGenerator),
CollectionModelProvider(idGenerator),
ArrayModelProvider(idGenerator),
EnumModelProvider,
ConstantsModelProvider,
StringConstantModelProvider,
CharToStringModelProvider,
PrimitivesModelProvider,
PrimitiveWrapperModelProvider,
)
}

private class SimpleIdGenerator : ToIntFunction<ClassId> {
/**
* Creates a model provider for [ObjectModelProvider] that generates values for object constructor.
*/
fun objectModelProviders(idGenerator: IntSupplier = SimpleIdGenerator()): ModelProvider {
return ModelProvider.of(
CollectionModelProvider(idGenerator),
ArrayModelProvider(idGenerator),
EnumModelProvider,
StringConstantModelProvider,
CharToStringModelProvider,
ConstantsModelProvider,
PrimitiveDefaultsModelProvider,
PrimitiveWrapperModelProvider,
)
}

private class SimpleIdGenerator : IntSupplier {
private val id = AtomicInteger()
override fun applyAsInt(value: ClassId?) = id.incrementAndGet()
override fun getAsInt() = id.incrementAndGet()
}
16 changes: 16 additions & 0 deletions utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/ModelProvider.kt
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,18 @@ fun interface ModelProvider {
fun of(vararg providers: ModelProvider): ModelProvider {
return Combined(providers.toList())
}

fun BiConsumer<Int, UtModel>.consumeAll(indices: List<Int>, models: Sequence<UtModel>) {
models.forEach { model ->
indices.forEach { index ->
accept(index, model)
}
}
}

fun BiConsumer<Int, UtModel>.consumeAll(indices: List<Int>, models: List<UtModel>) {
consumeAll(indices, models.asSequence())
}
}

/**
Expand All @@ -114,4 +126,8 @@ fun interface ModelProvider {
}
}
}
}

inline fun <reified T> ModelProvider.exceptIsInstance(): ModelProvider {
return except { it is T }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package org.utbot.fuzzer.providers

import org.utbot.framework.plugin.api.UtArrayModel
import org.utbot.framework.plugin.api.UtModel
import org.utbot.framework.plugin.api.util.defaultValueModel
import org.utbot.framework.plugin.api.util.isArray
import org.utbot.fuzzer.FuzzedMethodDescription
import org.utbot.fuzzer.ModelProvider
import org.utbot.fuzzer.ModelProvider.Companion.consumeAll
import java.util.function.BiConsumer
import java.util.function.IntSupplier

class ArrayModelProvider(
private val idGenerator: IntSupplier
) : ModelProvider {
override fun generate(description: FuzzedMethodDescription, consumer: BiConsumer<Int, UtModel>) {
description.parametersMap
.asSequence()
.filter { (classId, _) -> classId.isArray }
.forEach { (arrayClassId, indices) ->
consumer.consumeAll(indices, listOf(0, 10).map { arraySize ->
UtArrayModel(
id = idGenerator.asInt,
arrayClassId,
length = arraySize,
arrayClassId.elementClassId!!.defaultValueModel(),
mutableMapOf()
)
})
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.utbot.fuzzer.providers

import org.utbot.framework.plugin.api.UtModel
import org.utbot.framework.plugin.api.UtPrimitiveModel
import org.utbot.framework.plugin.api.util.charClassId
import org.utbot.framework.plugin.api.util.stringClassId
import org.utbot.fuzzer.FuzzedMethodDescription
import org.utbot.fuzzer.ModelProvider
import java.util.function.BiConsumer

/**
* Collects all char constants and creates string with them.
*/
object CharToStringModelProvider : ModelProvider {
override fun generate(description: FuzzedMethodDescription, consumer: BiConsumer<Int, UtModel>) {
val indices = description.parametersMap[stringClassId] ?: return
if (indices.isNotEmpty()) {
val string = description.concreteValues.asSequence()
.filter { it.classId == charClassId }
.map { it.value }
.filterIsInstance<Char>()
.joinToString(separator = "")
if (string.isNotEmpty()) {
val model = UtPrimitiveModel(string)
indices.forEach {
consumer.accept(it, model)
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package org.utbot.fuzzer.providers

import org.utbot.framework.plugin.api.ConstructorId
import org.utbot.framework.plugin.api.ExecutableId
import org.utbot.framework.plugin.api.MethodId
import org.utbot.framework.plugin.api.UtAssembleModel
import org.utbot.framework.plugin.api.UtExecutableCallModel
import org.utbot.framework.plugin.api.UtModel
import org.utbot.framework.plugin.api.UtStatementModel
import org.utbot.framework.plugin.api.util.id
import org.utbot.framework.plugin.api.util.jClass
import org.utbot.fuzzer.FuzzedMethodDescription
import org.utbot.fuzzer.ModelProvider
import org.utbot.fuzzer.ModelProvider.Companion.consumeAll
import java.util.function.BiConsumer
import java.util.function.IntSupplier

/**
* Provides different collection for concrete classes.
*
* For example, ArrayList, LinkedList, Collections.singletonList can be passed to check
* if that parameter breaks anything. For example, in case method doesn't expect
* a non-modifiable collection and tries to add values.
*/
class CollectionModelProvider(
private val idGenerator: IntSupplier
) : ModelProvider {

private val generators = mapOf(
java.util.List::class.java to ::createListModels,
java.util.Set::class.java to ::createSetModels,
java.util.Map::class.java to ::createMapModels,
java.util.Collection::class.java to ::createCollectionModels,
java.lang.Iterable::class.java to ::createCollectionModels,
java.util.Iterator::class.java to ::createIteratorModels,
)

override fun generate(description: FuzzedMethodDescription, consumer: BiConsumer<Int, UtModel>) {
description.parametersMap
.asSequence()
.forEach { (classId, indices) ->
generators[classId.jClass]?.let { createModels ->
consumer.consumeAll(indices, createModels())
}
}
}

private fun createListModels(): List<UtAssembleModel> {
return listOf(
java.util.List::class.java.createdBy(java.util.ArrayList::class.java.asConstructor()),
java.util.List::class.java.createdBy(java.util.LinkedList::class.java.asConstructor()),
java.util.List::class.java.createdBy(java.util.Collections::class.java.methodCall("emptyList", java.util.List::class.java)),
java.util.List::class.java.createdBy(java.util.Collections::class.java.methodCall("synchronizedList", java.util.List::class.java, params = listOf(java.util.List::class.java)), listOf(
java.util.List::class.java.createdBy(java.util.ArrayList::class.java.asConstructor())
)),
)
}

private fun createSetModels(): List<UtAssembleModel> {
return listOf(
java.util.Set::class.java.createdBy(java.util.HashSet::class.java.asConstructor()),
java.util.Set::class.java.createdBy(java.util.TreeSet::class.java.asConstructor()),
java.util.Set::class.java.createdBy(java.util.Collections::class.java.methodCall("emptySet", java.util.Set::class.java))
)
}

private fun createMapModels(): List<UtAssembleModel> {
return listOf(
java.util.Map::class.java.createdBy(java.util.HashMap::class.java.asConstructor()),
java.util.Map::class.java.createdBy(java.util.TreeMap::class.java.asConstructor()),
java.util.Map::class.java.createdBy(java.util.Collections::class.java.methodCall("emptyMap", java.util.Map::class.java)),
)
}

private fun createCollectionModels(): List<UtAssembleModel> {
return listOf(
java.util.Collection::class.java.createdBy(java.util.ArrayList::class.java.asConstructor()),
java.util.Collection::class.java.createdBy(java.util.HashSet::class.java.asConstructor()),
java.util.Collection::class.java.createdBy(java.util.Collections::class.java.methodCall("emptySet", java.util.Set::class.java)),
)
}

private fun createIteratorModels(): List<UtAssembleModel> {
return listOf(
java.util.Iterator::class.java.createdBy(java.util.Collections::class.java.methodCall("emptyIterator", java.util.Iterator::class.java)),
)
}

private fun Class<*>.asConstructor() = ConstructorId(id, emptyList())

private fun Class<*>.methodCall(methodName: String, returnType: Class<*>, params: List<Class<*>> = emptyList()) = MethodId(id, methodName, returnType.id, params.map { it.id })

private fun Class<*>.createdBy(init: ExecutableId, params: List<UtModel> = emptyList()): UtAssembleModel {
val instantiationChain = mutableListOf<UtStatementModel>()
val genId = idGenerator.asInt
return UtAssembleModel(
genId,
id,
"${init.classId.name}${init.parameters}#" + genId.toString(16),
instantiationChain
).apply {
instantiationChain += UtExecutableCallModel(null, init, params, this)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.utbot.fuzzer.providers

import org.utbot.framework.plugin.api.UtEnumConstantModel
import org.utbot.framework.plugin.api.UtModel
import org.utbot.framework.plugin.api.util.id
import org.utbot.framework.plugin.api.util.isSubtypeOf
import org.utbot.framework.plugin.api.util.jClass
import org.utbot.fuzzer.FuzzedMethodDescription
import org.utbot.fuzzer.ModelProvider
import org.utbot.fuzzer.ModelProvider.Companion.consumeAll
import java.util.function.BiConsumer

object EnumModelProvider : ModelProvider {
override fun generate(description: FuzzedMethodDescription, consumer: BiConsumer<Int, UtModel>) {
description.parametersMap
.asSequence()
.filter { (classId, _) -> classId.isSubtypeOf(Enum::class.java.id) }
.forEach { (classId, indices) ->
consumer.consumeAll(indices, classId.jClass.enumConstants.filterIsInstance<Enum<*>>().map {
UtEnumConstantModel(classId, it)
})
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@ import java.util.function.BiConsumer
@Suppress("unused") // disabled until fuzzer breaks test with null/nonnull annotations
object NullModelProvider : ModelProvider {
override fun generate(description: FuzzedMethodDescription, consumer: BiConsumer<Int, UtModel>) {
description.parameters
description.parametersMap
.asSequence()
.filter { classId -> classId.isRefType }
.forEachIndexed { index, classId ->
consumer.accept(index, UtNullModel(classId))
.filter { (classId, _) -> classId.isRefType }
.forEach { (classId, indices) ->
val model = UtNullModel(classId)
indices.forEach { consumer.accept(it, model) }
}
}
}
Loading