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

Other class is not mocked as required #747 #1033

Merged
merged 3 commits into from
Oct 11, 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 @@ -158,7 +158,7 @@ class UtBotSymbolicEngine(
private val methodUnderTest: ExecutableId,
classpath: String,
dependencyPaths: String,
mockStrategy: MockStrategy = NO_MOCKS,
val mockStrategy: MockStrategy = NO_MOCKS,
chosenClassesToMockAlways: Set<ClassId>,
private val solverTimeoutInMillis: Int = checkSolverTimeoutMillis
) : UtContextInitializer() {
Expand Down Expand Up @@ -429,6 +429,7 @@ class UtBotSymbolicEngine(
val names = graph.body.method.tags.filterIsInstance<ParamNamesTag>().firstOrNull()?.names
parameterNameMap = { index -> names?.getOrNull(index) }
fuzzerType = { try { toFuzzerType(methodUnderTest.executable.genericParameterTypes[it]) } catch (_: Throwable) { null } }
shouldMock = { mockStrategy.eligibleToMock(it, classUnderTest) }
}
val coveredInstructionTracker = Trie(Instruction::id)
val coveredInstructionValues = linkedMapOf<Trie.Node<Instruction>, List<FuzzedValue>>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ class FuzzedMethodDescription(
*/
var fuzzerType: (Int) -> FuzzedType? = { null }

/**
* Returns true if class should be mocked.
*/
var shouldMock: (ClassId) -> Boolean = { false }

/**
* Map class id to indices of this class in parameters list.
*/
Expand Down
27 changes: 12 additions & 15 deletions utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/FuzzedValue.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,19 @@ import org.utbot.framework.plugin.api.UtModel
/**
* Fuzzed Value stores information about concrete UtModel, reference to [ModelProvider]
* and reasons about why this value was generated.
*
* [summary] is a piece of useful information that clarify why this value has a concrete value.
*
* It supports a special character `%var%` that is used as a placeholder for parameter name.
*
* For example:
* 1. `%var% = 2` for a value that have value 2
* 2. `%var% >= 4` for a value that shouldn't be less than 4
* 3. `foo(%var%) returns true` for values that should be passed as a function parameter
* 4. `%var% has special characters` to describe content
*/
open class FuzzedValue(
val model: UtModel,
val createdBy: ModelProvider? = null,
) {

/**
* Summary is a piece of useful information that clarify why this value has a concrete value.
*
* It supports a special character `%var%` that is used as a placeholder for parameter name.
*
* For example:
* 1. `%var% = 2` for a value that have value 2
* 2. `%var% >= 4` for a value that shouldn't be less than 4
* 3. `foo(%var%) returns true` for values that should be passed as a function parameter
* 4. `%var% has special characters` to describe content
*/
var summary: String? = null
}
var summary: String? = null,
)
6 changes: 5 additions & 1 deletion utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/Fuzzer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import org.utbot.framework.plugin.api.util.voidClassId
import org.utbot.fuzzer.mutators.NumberRandomMutator
import org.utbot.fuzzer.mutators.RegexStringModelMutator
import org.utbot.fuzzer.mutators.StringRandomMutator
import org.utbot.fuzzer.objects.replaceWithMock
import org.utbot.fuzzer.providers.ArrayModelProvider
import org.utbot.fuzzer.providers.CharToStringModelProvider
import org.utbot.fuzzer.providers.CollectionWithEmptyStatesModelProvider
Expand Down Expand Up @@ -117,7 +118,10 @@ fun fuzz(description: FuzzedMethodDescription, vararg modelProviders: ModelProvi
val values = List<MutableList<FuzzedValue>>(description.parameters.size) { mutableListOf() }
modelProviders.forEach { fuzzingProvider ->
fuzzingProvider.generate(description).forEach { (index, model) ->
values[index].add(model)
val mock = replaceWithMock(model.model, description.shouldMock)
values[index].add(FuzzedValue(mock, model.createdBy).apply {
summary = model.summary
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is it not a part of the constructor?

})
}
}
description.parameters.forEachIndexed { index, classId ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ package org.utbot.fuzzer.objects
import org.utbot.framework.plugin.api.ClassId
import org.utbot.framework.plugin.api.ConstructorId
import org.utbot.framework.plugin.api.ExecutableId
import org.utbot.framework.plugin.api.FieldId
import org.utbot.framework.plugin.api.MethodId
import org.utbot.framework.plugin.api.UtAssembleModel
import org.utbot.framework.plugin.api.UtDirectSetFieldModel
import org.utbot.framework.plugin.api.UtExecutableCallModel
import org.utbot.framework.plugin.api.UtModel
import org.utbot.framework.plugin.api.UtStatementModel
Expand Down Expand Up @@ -38,6 +40,7 @@ class AssembleModelDsl internal constructor(
val call = KeyWord.Call
val constructor = KeyWord.Constructor(classId)
val method = KeyWord.Method(classId)
val field = KeyWord.Field(classId)

var id: () -> Int? = { null }
var name: (Int?) -> String = { "<dsl generated model>" }
Expand All @@ -53,10 +56,15 @@ class AssembleModelDsl internal constructor(

infix fun <T : ExecutableId> KeyWord.Call.instance(executableId: T) = CallDsl(executableId, false)

infix fun <T : FieldId> KeyWord.Call.instance(field: T) = FieldDsl(field, false)

infix fun <T : ExecutableId> KeyWord.Using.static(executableId: T) = UsingDsl(executableId)

infix fun <T : ExecutableId> KeyWord.Call.static(executableId: T) = CallDsl(executableId, true)

infix fun <T : FieldId> KeyWord.Call.static(field: T) = FieldDsl(field, true)

@Suppress("UNUSED_PARAMETER")
infix fun KeyWord.Using.empty(ignored: KeyWord.Constructor) {
initialization = { UtExecutableCallModel(null, ConstructorId(classId, emptyList()), emptyList()) }
}
Expand All @@ -73,6 +81,10 @@ class AssembleModelDsl internal constructor(
modChain += { UtExecutableCallModel(it, executableId, models.toList()) }
}

infix fun FieldDsl.with(model: UtModel) {
modChain += { UtDirectSetFieldModel(it, fieldId, model) }
}

internal fun build(): UtAssembleModel {
val objectId = id()
return UtAssembleModel(
Expand Down Expand Up @@ -102,8 +114,14 @@ class AssembleModelDsl internal constructor(
return MethodId(classId, name, returns, params)
}
}
class Field(val classId: ClassId) : KeyWord() {
operator fun invoke(name: String): FieldId {
return FieldId(classId, name)
}
}
}

class UsingDsl(val executableId: ExecutableId)
class CallDsl(val executableId: ExecutableId, val isStatic: Boolean)
class FieldDsl(val fieldId: FieldId, val isStatic: Boolean)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package org.utbot.fuzzer.objects

import org.utbot.framework.plugin.api.ClassId
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.UtCompositeModel
import org.utbot.framework.plugin.api.UtDirectSetFieldModel
import org.utbot.framework.plugin.api.UtExecutableCallModel
import org.utbot.framework.plugin.api.UtModel
import org.utbot.framework.plugin.api.UtStatementModel

/**
* Implements [MethodId] but also can supply a mock for this execution.
*
* Simplest example: setter and getter,
* when this methodId is a setter, getter can be used for a mock to supply correct value.
*/
internal class FuzzerMockableMethodId(
classId: ClassId,
name: String,
returnType: ClassId,
parameters: List<ClassId>,
val mock: () -> Map<ExecutableId, List<UtModel>> = { emptyMap() },
) : MethodId(classId, name, returnType, parameters) {

constructor(copyOf: MethodId, mock: () -> Map<ExecutableId, List<UtModel>> = { emptyMap() }) : this(
copyOf.classId, copyOf.name, copyOf.returnType, copyOf.parameters, mock
)

}

internal fun MethodId.toFuzzerMockable(block: suspend SequenceScope<Pair<MethodId, List<UtModel>>>.() -> Unit): FuzzerMockableMethodId {
return FuzzerMockableMethodId(this) {
sequence { block() }.toMap()
}
}

internal fun replaceWithMock(assembleModel: UtModel, shouldMock: (ClassId) -> Boolean): UtModel = when {
assembleModel !is UtAssembleModel -> assembleModel
shouldMock(assembleModel.classId) -> createMockModelFromFuzzerMockable(assembleModel, shouldMock)
else -> updateInnerModels(assembleModel, shouldMock)
}

private fun createMockModelFromFuzzerMockable(model: UtAssembleModel, shouldMock: (ClassId) -> Boolean): UtCompositeModel {
val mock = UtCompositeModel(model.id, model.classId, true)
for (mutator in model.modificationsChain) {
if (mutator is UtDirectSetFieldModel) {
mock.fields[mutator.fieldId] = replaceWithMock(mutator.fieldModel, shouldMock)
}
if (mutator is UtExecutableCallModel && mutator.executable is FuzzerMockableMethodId) {
(mutator.executable as FuzzerMockableMethodId).mock().forEach { (executionId, models) ->
mock.mocks[executionId] = models.map { p -> replaceWithMock(p, shouldMock) }
}
}
}
return mock
}

private fun updateInnerModels(model: UtAssembleModel, shouldMock: (ClassId) -> Boolean): UtAssembleModel {
val models = model.modificationsChain.map { call ->
var mockedStatementModel: UtStatementModel? = null
when (call) {
is UtDirectSetFieldModel -> {
val mock = replaceWithMock(call.fieldModel, shouldMock)
if (mock != call.fieldModel) {
mockedStatementModel = UtDirectSetFieldModel(call.instance, call.fieldId, mock)
}
}
is UtExecutableCallModel -> {
val params = call.params.map { m -> replaceWithMock(m, shouldMock) }
if (params != call.params) {
mockedStatementModel = UtExecutableCallModel(call.instance, call.executable, params)
}
}
}
mockedStatementModel ?: call
}
return with(model) {
UtAssembleModel(id, classId, modelName, instantiationCall, origin) { models }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import org.utbot.fuzzer.FuzzedMethodDescription
import org.utbot.fuzzer.FuzzedType
import org.utbot.fuzzer.FuzzedValue
import org.utbot.fuzzer.IdentityPreservingIdGenerator
import org.utbot.fuzzer.objects.FuzzerMockableMethodId
import org.utbot.fuzzer.objects.assembleModel

/**
Expand Down Expand Up @@ -101,11 +102,22 @@ class ObjectModelProvider(
)
field.setter != null -> UtExecutableCallModel(
fuzzedModel.model,
MethodId(
FuzzerMockableMethodId(
constructorId.classId,
field.setter.name,
field.setter.returnType.id,
listOf(field.classId)
listOf(field.classId),
mock = {
field.getter?.let { g ->
val getterMethodID = MethodId(
classId = constructorId.classId,
name = g.name,
returnType = g.returnType.id,
parameters = emptyList()
)
mapOf(getterMethodID to listOf(value.model))
} ?: emptyMap()
}
),
listOf(value.model)
)
Expand Down Expand Up @@ -144,16 +156,23 @@ class ObjectModelProvider(
private fun findSuitableFields(classId: ClassId, description: FuzzedMethodDescription): List<FieldDescription> {
val jClass = classId.jClass
return jClass.declaredFields.map { field ->
val setterAndGetter = jClass.findPublicSetterGetterIfHasPublicGetter(field, description)
FieldDescription(
field.name,
field.type.id,
isAccessible(field, description.packageName) && !isFinal(field.modifiers) && !isStatic(field.modifiers),
jClass.findPublicSetterIfHasPublicGetter(field, description)
name = field.name,
classId = field.type.id,
canBeSetDirectly = isAccessible(field, description.packageName) && !isFinal(field.modifiers) && !isStatic(field.modifiers),
setter = setterAndGetter?.setter,
getter = setterAndGetter?.getter,
)
}
}

private fun Class<*>.findPublicSetterIfHasPublicGetter(field: Field, description: FuzzedMethodDescription): Method? {
private class PublicSetterGetter(
val setter: Method,
val getter: Method,
)

private fun Class<*>.findPublicSetterGetterIfHasPublicGetter(field: Field, description: FuzzedMethodDescription): PublicSetterGetter? {
val postfixName = field.name.capitalize()
val setterName = "set$postfixName"
val getterName = "get$postfixName"
Expand All @@ -164,7 +183,7 @@ class ObjectModelProvider(
it.name == setterName &&
it.parameterCount == 1 &&
it.parameterTypes[0] == field.type
}
}?.let { PublicSetterGetter(it, getter) }
} else {
null
}
Expand All @@ -184,6 +203,7 @@ class ObjectModelProvider(
val classId: ClassId,
val canBeSetDirectly: Boolean,
val setter: Method?,
val getter: Method?
)
}
}
Loading