From 14f4b94bc25f6c7eb2040d63852e3589132e8e9d Mon Sep 17 00:00:00 2001 From: Maksim Pelevin Date: Tue, 27 Sep 2022 16:32:21 +0300 Subject: [PATCH 1/3] Other class is not mocked as required #747 --- .../org/utbot/engine/UtBotSymbolicEngine.kt | 3 +- .../utbot/fuzzer/FuzzedMethodDescription.kt | 5 + .../main/kotlin/org/utbot/fuzzer/Fuzzer.kt | 6 +- .../fuzzer/objects/AssembleModelUtils.kt | 56 ++++++++ .../fuzzer/objects/FuzzerMockableMethodId.kt | 32 +++++ .../fuzzer/providers/ObjectModelProvider.kt | 25 +++- .../api/MockOfObjectModelProviderTest.kt | 123 ++++++++++++++++++ 7 files changed, 243 insertions(+), 7 deletions(-) create mode 100644 utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/objects/FuzzerMockableMethodId.kt create mode 100644 utbot-fuzzers/src/test/kotlin/org/utbot/framework/plugin/api/MockOfObjectModelProviderTest.kt diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt index 86083c30b3..03f56a598f 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt @@ -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, private val solverTimeoutInMillis: Int = checkSolverTimeoutMillis ) : UtContextInitializer() { @@ -429,6 +429,7 @@ class UtBotSymbolicEngine( val names = graph.body.method.tags.filterIsInstance().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, List>() diff --git a/utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/FuzzedMethodDescription.kt b/utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/FuzzedMethodDescription.kt index 054a564e1a..0d87b21210 100644 --- a/utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/FuzzedMethodDescription.kt +++ b/utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/FuzzedMethodDescription.kt @@ -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. */ diff --git a/utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/Fuzzer.kt b/utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/Fuzzer.kt index 7f0f699511..a746c6f938 100644 --- a/utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/Fuzzer.kt +++ b/utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/Fuzzer.kt @@ -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.replaceToMock import org.utbot.fuzzer.providers.ArrayModelProvider import org.utbot.fuzzer.providers.CharToStringModelProvider import org.utbot.fuzzer.providers.CollectionWithEmptyStatesModelProvider @@ -117,7 +118,10 @@ fun fuzz(description: FuzzedMethodDescription, vararg modelProviders: ModelProvi val values = List>(description.parameters.size) { mutableListOf() } modelProviders.forEach { fuzzingProvider -> fuzzingProvider.generate(description).forEach { (index, model) -> - values[index].add(model) + val mock = replaceToMock(model.model, description.shouldMock) + values[index].add(FuzzedValue(mock, model.createdBy).apply { + summary = model.summary + }) } } description.parameters.forEachIndexed { index, classId -> diff --git a/utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/objects/AssembleModelUtils.kt b/utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/objects/AssembleModelUtils.kt index 0f05bacf8e..1f701aae91 100644 --- a/utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/objects/AssembleModelUtils.kt +++ b/utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/objects/AssembleModelUtils.kt @@ -3,8 +3,11 @@ 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.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 @@ -25,6 +28,43 @@ fun ModelProvider.assembleModel(id: Int, constructorId: ConstructorId, params: L } } +fun replaceToMock(assembleModel: UtModel, shouldMock: (ClassId) -> Boolean): UtModel { + if (assembleModel !is UtAssembleModel) return assembleModel + if (shouldMock(assembleModel.classId)) { + return UtCompositeModel(assembleModel.id, assembleModel.classId, true).apply { + assembleModel.modificationsChain.forEach { + if (it is UtDirectSetFieldModel) { + fields[it.fieldId] = replaceToMock(it.fieldModel, shouldMock) + } + if (it is UtExecutableCallModel && it.executable is FuzzerMockableMethodId) { + (it.executable as FuzzerMockableMethodId).mock().forEach { (executionId, models) -> + mocks[executionId] = models.map { p -> replaceToMock(p, shouldMock) } + } + } + } + } + } else { + val models = assembleModel.modificationsChain.map { call -> + var mockedStatementModel: UtStatementModel? = null + if (call is UtDirectSetFieldModel) { + val mock = replaceToMock(call.fieldModel, shouldMock) + if (mock != call.fieldModel) { + mockedStatementModel = UtDirectSetFieldModel(call.instance, call.fieldId, mock) + } + } else if (call is UtExecutableCallModel) { + val params = call.params.map { m -> replaceToMock(m, shouldMock) } + if (params != call.params) { + mockedStatementModel = UtExecutableCallModel(call.instance, call.executable, params) + } + } + mockedStatementModel ?: call + } + return with(assembleModel) { + UtAssembleModel(id, classId, modelName, instantiationCall, origin) { models } + } + } +} + fun ClassId.create( block: AssembleModelDsl.() -> Unit ): UtAssembleModel { @@ -38,6 +78,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 = { "" } @@ -53,10 +94,15 @@ class AssembleModelDsl internal constructor( infix fun KeyWord.Call.instance(executableId: T) = CallDsl(executableId, false) + infix fun KeyWord.Call.instance(field: T) = FieldDsl(field, false) + infix fun KeyWord.Using.static(executableId: T) = UsingDsl(executableId) infix fun KeyWord.Call.static(executableId: T) = CallDsl(executableId, true) + infix fun 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()) } } @@ -73,6 +119,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( @@ -102,8 +152,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) } \ No newline at end of file diff --git a/utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/objects/FuzzerMockableMethodId.kt b/utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/objects/FuzzerMockableMethodId.kt new file mode 100644 index 0000000000..16cc0f9685 --- /dev/null +++ b/utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/objects/FuzzerMockableMethodId.kt @@ -0,0 +1,32 @@ +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.UtModel + +/** + * 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, + val mock: () -> Map> = { emptyMap() }, +) : MethodId(classId, name, returnType, parameters) { + + constructor(copyOf: MethodId, mock: () -> Map> = { emptyMap() }) : this( + copyOf.classId, copyOf.name, copyOf.returnType, copyOf.parameters, mock + ) + +} + +internal fun MethodId.toFuzzerMockable(block: suspend SequenceScope>>.() -> Unit): FuzzerMockableMethodId { + return FuzzerMockableMethodId(this) { + sequence { block() }.toMap() + } +} \ No newline at end of file diff --git a/utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/providers/ObjectModelProvider.kt b/utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/providers/ObjectModelProvider.kt index 8c612e3866..1e52421839 100644 --- a/utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/providers/ObjectModelProvider.kt +++ b/utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/providers/ObjectModelProvider.kt @@ -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 /** @@ -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( + constructorId.classId, + g.name, + g.returnType.id, + emptyList() + ) + mapOf(getterMethodID to listOf(value.model)) + } ?: emptyMap() + } ), listOf(value.model) ) @@ -144,16 +156,18 @@ class ObjectModelProvider( private fun findSuitableFields(classId: ClassId, description: FuzzedMethodDescription): List { 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) + setterAndGetter?.first, + setterAndGetter?.second, ) } } - private fun Class<*>.findPublicSetterIfHasPublicGetter(field: Field, description: FuzzedMethodDescription): Method? { + private fun Class<*>.findPublicSetterGetterIfHasPublicGetter(field: Field, description: FuzzedMethodDescription): Pair? { val postfixName = field.name.capitalize() val setterName = "set$postfixName" val getterName = "get$postfixName" @@ -164,7 +178,7 @@ class ObjectModelProvider( it.name == setterName && it.parameterCount == 1 && it.parameterTypes[0] == field.type - } + }?.let { it to getter } } else { null } @@ -184,6 +198,7 @@ class ObjectModelProvider( val classId: ClassId, val canBeSetDirectly: Boolean, val setter: Method?, + val getter: Method? ) } } \ No newline at end of file diff --git a/utbot-fuzzers/src/test/kotlin/org/utbot/framework/plugin/api/MockOfObjectModelProviderTest.kt b/utbot-fuzzers/src/test/kotlin/org/utbot/framework/plugin/api/MockOfObjectModelProviderTest.kt new file mode 100644 index 0000000000..c440a944e5 --- /dev/null +++ b/utbot-fuzzers/src/test/kotlin/org/utbot/framework/plugin/api/MockOfObjectModelProviderTest.kt @@ -0,0 +1,123 @@ +package org.utbot.framework.plugin.api + +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Test +import org.utbot.framework.plugin.api.util.UtContext +import org.utbot.framework.plugin.api.util.doubleWrapperClassId +import org.utbot.framework.plugin.api.util.id +import org.utbot.framework.plugin.api.util.voidClassId +import org.utbot.framework.plugin.api.util.withUtContext +import org.utbot.fuzzer.FuzzedMethodDescription +import org.utbot.fuzzer.objects.create +import org.utbot.fuzzer.objects.replaceToMock +import org.utbot.fuzzer.objects.toFuzzerMockable +import org.utbot.fuzzer.providers.ObjectModelProvider + +class MockOfObjectModelProviderTest { + + class Some { + @Suppress("unused") + var another: Some? = null + } + + @Test + fun `no mock is generated by default`() = withContext { + val description = FuzzedMethodDescription("test", voidClassId, listOf(Some::class.id)) + val provider = ObjectModelProvider(TestIdentityPreservingIdGenerator) + val results = provider.generate(description).map { it.value.model }.map { + replaceToMock(it) { m -> description.shouldMock(m) } + }.toList() + assertEquals(2, results.size) + results.forEach { model -> + assertInstanceOf(UtAssembleModel::class.java, model) + } + assertEquals(0, (results[0] as UtAssembleModel).modificationsChain.size) + assertEquals(1, (results[1] as UtAssembleModel).modificationsChain.size) + } + + @Test + fun `mock is generated`() = withContext { + val description = FuzzedMethodDescription("test", voidClassId, listOf(Some::class.id)).apply { + shouldMock = { true } + } + val provider = ObjectModelProvider(TestIdentityPreservingIdGenerator) + val results = provider.generate(description).map { it.value.model }.map { + replaceToMock(it) { m -> description.shouldMock(m) } + }.toList() + assertEquals(2, results.size) + results.forEach { model -> + assertInstanceOf(UtCompositeModel::class.java, model) + assertTrue((model as UtCompositeModel).isMock) + } + } + + @Test + fun `mock is generated for several recursion level`() = withContext { + val description = FuzzedMethodDescription("test", voidClassId, listOf(Some::class.id)).apply { + shouldMock = { true } + } + val provider = ObjectModelProvider(TestIdentityPreservingIdGenerator, recursionDepthLeft = 2) + val results = provider.generate(description).map { it.value.model }.map { + replaceToMock(it) { m -> description.shouldMock(m) } + }.toList() + assertEquals(2, results.size) + results.forEach { model -> + assertInstanceOf(UtCompositeModel::class.java, model) + assertTrue((model as UtCompositeModel).isMock) + } + val modelWithFieldChanger = results[1] as UtCompositeModel + assertEquals(1, modelWithFieldChanger.mocks.size) + val entry = modelWithFieldChanger.mocks.entries.single() + assertEquals("getAnother", entry.key.name) + assertEquals(Some::class.id, entry.key.returnType) + assertEquals(1, entry.value.size) + assertInstanceOf(UtCompositeModel::class.java, entry.value.single()) + } + + @Test + fun `check field replaced with concrete values`() { + val customModel = Any::class.id.create { + using empty constructor + call instance field("some") with UtNullModel(Nothing::class.id) + } + val replacedModel = replaceToMock(customModel) { true } + assertInstanceOf(UtCompositeModel::class.java, replacedModel) + replacedModel as UtCompositeModel + assertEquals(0, replacedModel.mocks.size) + val fields = replacedModel.fields + assertEquals(1, fields.size) + val entry = fields.entries.single() + assertEquals("some", entry.key.name) + assertEquals(UtNullModel(Nothing::class.id), entry.value) + } + + @Test + fun `check method replaced with mock values`() { + val customModel = Any::class.id.create { + using empty constructor + call instance method("some").toFuzzerMockable { + yield(MethodId(classId, "another", doubleWrapperClassId, emptyList()) to listOf(UtPrimitiveModel(2.0))) + } with values(UtNullModel(Nothing::class.id)) + } + val replacedModel = replaceToMock(customModel) { true } + assertInstanceOf(UtCompositeModel::class.java, replacedModel) + replacedModel as UtCompositeModel + assertEquals(0, replacedModel.fields.size) + val mocks = replacedModel.mocks + assertEquals(1, replacedModel.mocks.size) + val (executableId, models) = mocks.entries.single() + assertEquals("another", executableId.name) + assertEquals(doubleWrapperClassId, executableId.returnType) + assertEquals(0, executableId.parameters.size) + assertEquals(1, models.size) + assertInstanceOf(UtPrimitiveModel::class.java, models.single()) + assertEquals(2.0, (models.single() as UtPrimitiveModel).value) + } + + private fun withContext(block: () -> T) { + withUtContext(UtContext(this::class.java.classLoader)) { + block() + } + } + +} \ No newline at end of file From 0299e13bf0395510bb4d020cc54c888e6b3cc7ec Mon Sep 17 00:00:00 2001 From: Maksim Pelevin Date: Tue, 4 Oct 2022 15:31:23 +0300 Subject: [PATCH 2/3] Fix tests --- .../framework/plugin/api/MockOfObjectModelProviderTest.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/utbot-fuzzers/src/test/kotlin/org/utbot/framework/plugin/api/MockOfObjectModelProviderTest.kt b/utbot-fuzzers/src/test/kotlin/org/utbot/framework/plugin/api/MockOfObjectModelProviderTest.kt index c440a944e5..081aaf3047 100644 --- a/utbot-fuzzers/src/test/kotlin/org/utbot/framework/plugin/api/MockOfObjectModelProviderTest.kt +++ b/utbot-fuzzers/src/test/kotlin/org/utbot/framework/plugin/api/MockOfObjectModelProviderTest.kt @@ -31,8 +31,8 @@ class MockOfObjectModelProviderTest { results.forEach { model -> assertInstanceOf(UtAssembleModel::class.java, model) } - assertEquals(0, (results[0] as UtAssembleModel).modificationsChain.size) - assertEquals(1, (results[1] as UtAssembleModel).modificationsChain.size) + assertEquals(0, (results[1] as UtAssembleModel).modificationsChain.size) + assertEquals(1, (results[0] as UtAssembleModel).modificationsChain.size) } @Test @@ -65,7 +65,7 @@ class MockOfObjectModelProviderTest { assertInstanceOf(UtCompositeModel::class.java, model) assertTrue((model as UtCompositeModel).isMock) } - val modelWithFieldChanger = results[1] as UtCompositeModel + val modelWithFieldChanger = results[0] as UtCompositeModel assertEquals(1, modelWithFieldChanger.mocks.size) val entry = modelWithFieldChanger.mocks.entries.single() assertEquals("getAnother", entry.key.name) From 23dc52a53c0acd949c820d069994c132cc980be3 Mon Sep 17 00:00:00 2001 From: Maksim Pelevin Date: Tue, 11 Oct 2022 15:30:45 +0300 Subject: [PATCH 3/3] Fixes after review --- .../kotlin/org/utbot/fuzzer/FuzzedValue.kt | 27 +++--- .../main/kotlin/org/utbot/fuzzer/Fuzzer.kt | 4 +- .../fuzzer/objects/AssembleModelUtils.kt | 38 --------- .../utbot/fuzzer/objects/FuzzerMockUtils.kt | 82 +++++++++++++++++++ .../fuzzer/objects/FuzzerMockableMethodId.kt | 32 -------- .../fuzzer/providers/ObjectModelProvider.kt | 27 +++--- .../api/MockOfObjectModelProviderTest.kt | 12 +-- 7 files changed, 118 insertions(+), 104 deletions(-) create mode 100644 utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/objects/FuzzerMockUtils.kt delete mode 100644 utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/objects/FuzzerMockableMethodId.kt diff --git a/utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/FuzzedValue.kt b/utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/FuzzedValue.kt index 55e78704a9..1da79b63e8 100644 --- a/utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/FuzzedValue.kt +++ b/utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/FuzzedValue.kt @@ -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 -} \ No newline at end of file + var summary: String? = null, +) \ No newline at end of file diff --git a/utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/Fuzzer.kt b/utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/Fuzzer.kt index a746c6f938..e9ebfca2e9 100644 --- a/utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/Fuzzer.kt +++ b/utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/Fuzzer.kt @@ -7,7 +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.replaceToMock +import org.utbot.fuzzer.objects.replaceWithMock import org.utbot.fuzzer.providers.ArrayModelProvider import org.utbot.fuzzer.providers.CharToStringModelProvider import org.utbot.fuzzer.providers.CollectionWithEmptyStatesModelProvider @@ -118,7 +118,7 @@ fun fuzz(description: FuzzedMethodDescription, vararg modelProviders: ModelProvi val values = List>(description.parameters.size) { mutableListOf() } modelProviders.forEach { fuzzingProvider -> fuzzingProvider.generate(description).forEach { (index, model) -> - val mock = replaceToMock(model.model, description.shouldMock) + val mock = replaceWithMock(model.model, description.shouldMock) values[index].add(FuzzedValue(mock, model.createdBy).apply { summary = model.summary }) diff --git a/utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/objects/AssembleModelUtils.kt b/utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/objects/AssembleModelUtils.kt index 1f701aae91..d17292fe55 100644 --- a/utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/objects/AssembleModelUtils.kt +++ b/utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/objects/AssembleModelUtils.kt @@ -6,7 +6,6 @@ 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.UtCompositeModel import org.utbot.framework.plugin.api.UtDirectSetFieldModel import org.utbot.framework.plugin.api.UtExecutableCallModel import org.utbot.framework.plugin.api.UtModel @@ -28,43 +27,6 @@ fun ModelProvider.assembleModel(id: Int, constructorId: ConstructorId, params: L } } -fun replaceToMock(assembleModel: UtModel, shouldMock: (ClassId) -> Boolean): UtModel { - if (assembleModel !is UtAssembleModel) return assembleModel - if (shouldMock(assembleModel.classId)) { - return UtCompositeModel(assembleModel.id, assembleModel.classId, true).apply { - assembleModel.modificationsChain.forEach { - if (it is UtDirectSetFieldModel) { - fields[it.fieldId] = replaceToMock(it.fieldModel, shouldMock) - } - if (it is UtExecutableCallModel && it.executable is FuzzerMockableMethodId) { - (it.executable as FuzzerMockableMethodId).mock().forEach { (executionId, models) -> - mocks[executionId] = models.map { p -> replaceToMock(p, shouldMock) } - } - } - } - } - } else { - val models = assembleModel.modificationsChain.map { call -> - var mockedStatementModel: UtStatementModel? = null - if (call is UtDirectSetFieldModel) { - val mock = replaceToMock(call.fieldModel, shouldMock) - if (mock != call.fieldModel) { - mockedStatementModel = UtDirectSetFieldModel(call.instance, call.fieldId, mock) - } - } else if (call is UtExecutableCallModel) { - val params = call.params.map { m -> replaceToMock(m, shouldMock) } - if (params != call.params) { - mockedStatementModel = UtExecutableCallModel(call.instance, call.executable, params) - } - } - mockedStatementModel ?: call - } - return with(assembleModel) { - UtAssembleModel(id, classId, modelName, instantiationCall, origin) { models } - } - } -} - fun ClassId.create( block: AssembleModelDsl.() -> Unit ): UtAssembleModel { diff --git a/utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/objects/FuzzerMockUtils.kt b/utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/objects/FuzzerMockUtils.kt new file mode 100644 index 0000000000..e2c97d0e43 --- /dev/null +++ b/utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/objects/FuzzerMockUtils.kt @@ -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, + val mock: () -> Map> = { emptyMap() }, +) : MethodId(classId, name, returnType, parameters) { + + constructor(copyOf: MethodId, mock: () -> Map> = { emptyMap() }) : this( + copyOf.classId, copyOf.name, copyOf.returnType, copyOf.parameters, mock + ) + +} + +internal fun MethodId.toFuzzerMockable(block: suspend SequenceScope>>.() -> 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 } + } +} \ No newline at end of file diff --git a/utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/objects/FuzzerMockableMethodId.kt b/utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/objects/FuzzerMockableMethodId.kt deleted file mode 100644 index 16cc0f9685..0000000000 --- a/utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/objects/FuzzerMockableMethodId.kt +++ /dev/null @@ -1,32 +0,0 @@ -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.UtModel - -/** - * 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, - val mock: () -> Map> = { emptyMap() }, -) : MethodId(classId, name, returnType, parameters) { - - constructor(copyOf: MethodId, mock: () -> Map> = { emptyMap() }) : this( - copyOf.classId, copyOf.name, copyOf.returnType, copyOf.parameters, mock - ) - -} - -internal fun MethodId.toFuzzerMockable(block: suspend SequenceScope>>.() -> Unit): FuzzerMockableMethodId { - return FuzzerMockableMethodId(this) { - sequence { block() }.toMap() - } -} \ No newline at end of file diff --git a/utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/providers/ObjectModelProvider.kt b/utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/providers/ObjectModelProvider.kt index 1e52421839..10b329d8f1 100644 --- a/utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/providers/ObjectModelProvider.kt +++ b/utbot-fuzzers/src/main/kotlin/org/utbot/fuzzer/providers/ObjectModelProvider.kt @@ -110,10 +110,10 @@ class ObjectModelProvider( mock = { field.getter?.let { g -> val getterMethodID = MethodId( - constructorId.classId, - g.name, - g.returnType.id, - emptyList() + classId = constructorId.classId, + name = g.name, + returnType = g.returnType.id, + parameters = emptyList() ) mapOf(getterMethodID to listOf(value.model)) } ?: emptyMap() @@ -158,16 +158,21 @@ class ObjectModelProvider( 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), - setterAndGetter?.first, - setterAndGetter?.second, + 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<*>.findPublicSetterGetterIfHasPublicGetter(field: Field, description: FuzzedMethodDescription): Pair? { + 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" @@ -178,7 +183,7 @@ class ObjectModelProvider( it.name == setterName && it.parameterCount == 1 && it.parameterTypes[0] == field.type - }?.let { it to getter } + }?.let { PublicSetterGetter(it, getter) } } else { null } diff --git a/utbot-fuzzers/src/test/kotlin/org/utbot/framework/plugin/api/MockOfObjectModelProviderTest.kt b/utbot-fuzzers/src/test/kotlin/org/utbot/framework/plugin/api/MockOfObjectModelProviderTest.kt index 081aaf3047..d24e85e98e 100644 --- a/utbot-fuzzers/src/test/kotlin/org/utbot/framework/plugin/api/MockOfObjectModelProviderTest.kt +++ b/utbot-fuzzers/src/test/kotlin/org/utbot/framework/plugin/api/MockOfObjectModelProviderTest.kt @@ -9,7 +9,7 @@ import org.utbot.framework.plugin.api.util.voidClassId import org.utbot.framework.plugin.api.util.withUtContext import org.utbot.fuzzer.FuzzedMethodDescription import org.utbot.fuzzer.objects.create -import org.utbot.fuzzer.objects.replaceToMock +import org.utbot.fuzzer.objects.replaceWithMock import org.utbot.fuzzer.objects.toFuzzerMockable import org.utbot.fuzzer.providers.ObjectModelProvider @@ -25,7 +25,7 @@ class MockOfObjectModelProviderTest { val description = FuzzedMethodDescription("test", voidClassId, listOf(Some::class.id)) val provider = ObjectModelProvider(TestIdentityPreservingIdGenerator) val results = provider.generate(description).map { it.value.model }.map { - replaceToMock(it) { m -> description.shouldMock(m) } + replaceWithMock(it) { m -> description.shouldMock(m) } }.toList() assertEquals(2, results.size) results.forEach { model -> @@ -42,7 +42,7 @@ class MockOfObjectModelProviderTest { } val provider = ObjectModelProvider(TestIdentityPreservingIdGenerator) val results = provider.generate(description).map { it.value.model }.map { - replaceToMock(it) { m -> description.shouldMock(m) } + replaceWithMock(it) { m -> description.shouldMock(m) } }.toList() assertEquals(2, results.size) results.forEach { model -> @@ -58,7 +58,7 @@ class MockOfObjectModelProviderTest { } val provider = ObjectModelProvider(TestIdentityPreservingIdGenerator, recursionDepthLeft = 2) val results = provider.generate(description).map { it.value.model }.map { - replaceToMock(it) { m -> description.shouldMock(m) } + replaceWithMock(it) { m -> description.shouldMock(m) } }.toList() assertEquals(2, results.size) results.forEach { model -> @@ -80,7 +80,7 @@ class MockOfObjectModelProviderTest { using empty constructor call instance field("some") with UtNullModel(Nothing::class.id) } - val replacedModel = replaceToMock(customModel) { true } + val replacedModel = replaceWithMock(customModel) { true } assertInstanceOf(UtCompositeModel::class.java, replacedModel) replacedModel as UtCompositeModel assertEquals(0, replacedModel.mocks.size) @@ -99,7 +99,7 @@ class MockOfObjectModelProviderTest { yield(MethodId(classId, "another", doubleWrapperClassId, emptyList()) to listOf(UtPrimitiveModel(2.0))) } with values(UtNullModel(Nothing::class.id)) } - val replacedModel = replaceToMock(customModel) { true } + val replacedModel = replaceWithMock(customModel) { true } assertInstanceOf(UtCompositeModel::class.java, replacedModel) replacedModel as UtCompositeModel assertEquals(0, replacedModel.fields.size)