-
Notifications
You must be signed in to change notification settings - Fork 45
Other class is not mocked as required #747 #1033
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
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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) | ||
| } | ||
| } | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As for me, it is hard to read because of missing spaces. My suggestion is something like: fun replaceToMock(
assembleModel: UtModel,
shouldMock: (ClassId) -> Boolean
): UtModel = with(assembleModel) {
if (this !is UtAssembleModel) return this
if (shouldMock(classId)) {
UtCompositeModel(id, classId, isMock = true).apply {
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 = 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
}
with(assembleModel) {
UtAssembleModel(id, classId, modelName, instantiationCall, origin) { models }
}
}
} |
||
| 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 = { "<dsl generated model>" } | ||
|
|
@@ -53,10 +94,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()) } | ||
| } | ||
|
|
@@ -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) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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<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 { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you make a data class for these pairs?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "Pair" is used as convenient and well-known construction like "key to value". Because this method is used by dsl I'd prefer to keep it this way. Also, I have some doubt about using data classes for such small pairs. Maybe, typealias is more proper way in these cases? |
||
| return FuzzerMockableMethodId(this) { | ||
| sequence { block() }.toMap() | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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() | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please, use a named argument here |
||
| ) | ||
| mapOf(getterMethodID to listOf(value.model)) | ||
| } ?: emptyMap() | ||
| } | ||
| ), | ||
| listOf(value.model) | ||
| ) | ||
|
|
@@ -144,16 +156,18 @@ 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) | ||
| setterAndGetter?.first, | ||
| setterAndGetter?.second, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And here (a data class). From a browser, it's not obvious which one is what. |
||
| ) | ||
| } | ||
| } | ||
|
|
||
| private fun Class<*>.findPublicSetterIfHasPublicGetter(field: Field, description: FuzzedMethodDescription): Method? { | ||
| private fun Class<*>.findPublicSetterGetterIfHasPublicGetter(field: Field, description: FuzzedMethodDescription): Pair<Method, Method>? { | ||
| 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? | ||
| ) | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 <T> withContext(block: () -> T) { | ||
| withUtContext(UtContext(this::class.java.classLoader)) { | ||
| block() | ||
| } | ||
| } | ||
|
|
||
| } |
There was a problem hiding this comment.
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?