diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/context/CgContext.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/context/CgContext.kt index 9b578658d6..065256ebd3 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/context/CgContext.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/context/CgContext.kt @@ -20,11 +20,14 @@ import org.utbot.framework.codegen.domain.builtin.UtilClassFileMethodProvider import org.utbot.framework.codegen.domain.builtin.UtilMethodProvider import org.utbot.framework.codegen.domain.models.* import org.utbot.framework.codegen.services.access.Block +import org.utbot.framework.codegen.services.access.CgFieldStateManager import org.utbot.framework.codegen.tree.EnvironmentFieldStateCache import org.utbot.framework.codegen.tree.importIfNeeded import org.utbot.framework.plugin.api.BuiltinClassId import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.codegen.services.language.CgLanguageAssistant +import org.utbot.framework.codegen.tree.fieldmanager.CgAbstractClassFieldManager +import org.utbot.framework.codegen.tree.fieldmanager.CgClassFieldManager import org.utbot.framework.plugin.api.CodegenLanguage import org.utbot.framework.plugin.api.ExecutableId import org.utbot.framework.plugin.api.FieldId @@ -242,6 +245,12 @@ interface CgContextOwner { */ var successfulExecutionsModels: List + /** + * Managers to process annotated fields of the class under test + * relevant for the current generation type. + */ + val relevantFieldManagers: MutableList + fun block(init: () -> Unit): Block { val prevBlock = currentBlock return try { @@ -508,6 +517,7 @@ class CgContext( RuntimeExceptionTestsBehaviour.defaultItem, override val hangingTestsTimeout: HangingTestsTimeout = HangingTestsTimeout(), override val enableTestsTimeout: Boolean = true, + override val relevantFieldManagers: MutableList = mutableListOf(), override var containsReflectiveCall: Boolean = false, ) : CgContextOwner { override lateinit var statesCache: EnvironmentFieldStateCache diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/TestClassModel.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/TestClassModel.kt index ff4881e924..6cecbf68e9 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/TestClassModel.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/TestClassModel.kt @@ -20,20 +20,3 @@ class SimpleTestClassModel( nestedClasses: List = listOf(), ): TestClassModel(classUnderTest, methodTestSets, nestedClasses) -/** - * Extended [SimpleTestClassModel] for Spring analysis reasons - */ -class SpringTestClassModel( - classUnderTest: ClassId, - methodTestSets: List, - nestedClasses: List, - val springSpecificInformation: SpringSpecificInformation, -): TestClassModel(classUnderTest, methodTestSets, nestedClasses) - -class SpringSpecificInformation( - val thisInstanceModels: TypedModelWrappers, - val thisInstanceDependentMocks: TypedModelWrappers, - val thisInstanceDependentSpies: TypedModelWrappers, - val autowiredFromContextModels: TypedModelWrappers, - val entityManagerModels: TypedModelWrappers, -) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/builders/SimpleTestClassModelBuilder.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/builders/SimpleTestClassModelBuilder.kt index 111ed3764a..41f1b6824b 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/builders/SimpleTestClassModelBuilder.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/builders/SimpleTestClassModelBuilder.kt @@ -1,12 +1,14 @@ package org.utbot.framework.codegen.domain.models.builders -import org.utbot.framework.codegen.domain.context.CgContext +import org.utbot.framework.codegen.domain.UtModelWrapper import org.utbot.framework.codegen.domain.models.CgMethodTestSet import org.utbot.framework.codegen.domain.models.SimpleTestClassModel import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.util.enclosingClass -open class SimpleTestClassModelBuilder(context: CgContext): TestClassModelBuilder() { +typealias TypedModelWrappers = Map> + +open class SimpleTestClassModelBuilder: TestClassModelBuilder() { override fun createTestClassModel( classUnderTest: ClassId, testSets: List, diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/generator/CodeGenerator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/generator/CodeGenerator.kt index 5dc79b10e5..d43eb7079b 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/generator/CodeGenerator.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/generator/CodeGenerator.kt @@ -10,7 +10,7 @@ open class CodeGenerator(params: CodeGeneratorParams): AbstractCodeGenerator(par protected val classUnderTest: ClassId = params.classUnderTest override fun generate(testSets: List): CodeGeneratorResult { - val testClassModel = SimpleTestClassModelBuilder(context).createTestClassModel(classUnderTest, testSets) + val testClassModel = SimpleTestClassModelBuilder().createTestClassModel(classUnderTest, testSets) logger.info { "Code generation phase started at ${now()}" } val astConstructor = CgSimpleTestClassConstructor(context) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/generator/SpringCodeGenerator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/generator/SpringCodeGenerator.kt index 67be83d0b5..f33d63ec72 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/generator/SpringCodeGenerator.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/generator/SpringCodeGenerator.kt @@ -2,7 +2,7 @@ package org.utbot.framework.codegen.generator import org.utbot.framework.codegen.domain.context.CgContext import org.utbot.framework.codegen.domain.models.CgMethodTestSet -import org.utbot.framework.codegen.domain.models.builders.SpringTestClassModelBuilder +import org.utbot.framework.codegen.domain.models.builders.SimpleTestClassModelBuilder import org.utbot.framework.codegen.services.language.CgLanguageAssistant import org.utbot.framework.codegen.tree.CgCustomAssertConstructor import org.utbot.framework.codegen.tree.CgSpringIntegrationTestClassConstructor @@ -39,7 +39,7 @@ class SpringCodeGenerator( private val classUnderTest: ClassId = params.classUnderTest override fun generate(testSets: List): CodeGeneratorResult { - val testClassModel = SpringTestClassModelBuilder(context).createTestClassModel(classUnderTest, testSets) + val testClassModel = SimpleTestClassModelBuilder().createTestClassModel(classUnderTest, testSets) logger.info { "Code generation phase started at ${now()}" } val astConstructor = when (springTestType) { diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgAbstractSpringTestClassConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgAbstractSpringTestClassConstructor.kt index f5b011a1af..cc2f86b4f1 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgAbstractSpringTestClassConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgAbstractSpringTestClassConstructor.kt @@ -2,9 +2,8 @@ package org.utbot.framework.codegen.tree import org.utbot.framework.codegen.domain.builtin.TestClassUtilMethodProvider import org.utbot.framework.codegen.domain.context.CgContext -import org.utbot.framework.codegen.domain.models.AnnotationTarget.* +import org.utbot.framework.codegen.domain.models.AnnotationTarget.Method import org.utbot.framework.codegen.domain.models.CgClassBody -import org.utbot.framework.codegen.domain.models.CgDeclaration import org.utbot.framework.codegen.domain.models.CgFieldDeclaration import org.utbot.framework.codegen.domain.models.CgFrameworkUtilMethod import org.utbot.framework.codegen.domain.models.CgMethod @@ -13,21 +12,18 @@ import org.utbot.framework.codegen.domain.models.CgMethodsCluster import org.utbot.framework.codegen.domain.models.CgRegion import org.utbot.framework.codegen.domain.models.CgStatement import org.utbot.framework.codegen.domain.models.CgStaticsRegion -import org.utbot.framework.codegen.domain.models.CgVariable -import org.utbot.framework.codegen.domain.models.SpringTestClassModel -import org.utbot.framework.codegen.domain.models.builders.TypedModelWrappers +import org.utbot.framework.codegen.domain.models.SimpleTestClassModel +import org.utbot.framework.codegen.tree.fieldmanager.ClassFieldManagerFacade import org.utbot.framework.plugin.api.UtExecution -import org.utbot.framework.plugin.api.UtSpringContextModel import org.utbot.framework.plugin.api.util.id -import java.lang.Exception abstract class CgAbstractSpringTestClassConstructor(context: CgContext) : - CgAbstractTestClassConstructor(context) { + CgAbstractTestClassConstructor(context) { protected val variableConstructor: CgSpringVariableConstructor = CgComponents.getVariableConstructorBy(context) as CgSpringVariableConstructor - override fun constructTestClassBody(testClassModel: SpringTestClassModel): CgClassBody { + override fun constructTestClassBody(testClassModel: SimpleTestClassModel): CgClassBody { return buildClassBody(currentTestClass) { // TODO: support inner classes here @@ -80,7 +76,7 @@ abstract class CgAbstractSpringTestClassConstructor(context: CgContext) : return if (regions.any()) regions else null } - abstract fun constructClassFields(testClassModel: SpringTestClassModel): List + abstract fun constructClassFields(testClassModel: SimpleTestClassModel): List /** * Here "additional" means that these tests are not obtained from @@ -90,49 +86,6 @@ abstract class CgAbstractSpringTestClassConstructor(context: CgContext) : open fun constructAdditionalUtilMethods(): CgMethodsCluster? = null - protected fun constructFieldsWithAnnotation( - fieldManager: CgClassFieldManager, - groupedModelsByClassId: TypedModelWrappers, - ): List { - val annotationClassId = fieldManager.annotationType - val annotation = addAnnotation(annotationClassId, Field) - - val constructedDeclarations = mutableListOf() - for ((classId, modelWrappers) in groupedModelsByClassId) { - - val modelWrapper = modelWrappers.firstOrNull() ?: continue - val model = modelWrapper.model - - val fieldWithAnnotationIsRequired = fieldManager.fieldWithAnnotationIsRequired(model.classId) - if (!fieldWithAnnotationIsRequired) { - continue - } - - val baseVarName = fieldManager.constructBaseVarName(model) - - val createdVariable = variableConstructor.getOrCreateVariable(model, baseVarName) as? CgVariable - ?: error("`CgVariable` cannot be constructed from a $model model") - - val declaration = CgDeclaration(classId, variableName = createdVariable.name, initializer = null) - - constructedDeclarations += CgFieldDeclaration( - ownerClassId = currentTestClass, - declaration, - annotation - ) - - modelWrappers - .forEach { modelWrapper -> - - valueByUtModelWrapper[modelWrapper] = createdVariable - - variableConstructor.annotatedModelGroups - .getOrPut(annotationClassId) { mutableSetOf() } += modelWrapper - } - } - - return constructedDeclarations - } /** * Clears the results of variable instantiations that occurred @@ -145,8 +98,7 @@ abstract class CgAbstractSpringTestClassConstructor(context: CgContext) : * but it will take very long time to do it now. */ private fun clearUnwantedVariableModels() { - val trustedListOfModels = - variableConstructor.annotatedModelGroups.values.flatten() + listOf(UtSpringContextModel.wrap()) + val trustedListOfModels = ClassFieldManagerFacade(context).findTrustedModels() valueByUtModelWrapper .filterNot { it.key in trustedListOfModels } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgClassFieldManager.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgClassFieldManager.kt deleted file mode 100644 index e2c8a3b83f..0000000000 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgClassFieldManager.kt +++ /dev/null @@ -1,216 +0,0 @@ -package org.utbot.framework.codegen.tree - -import org.utbot.framework.codegen.domain.UtModelWrapper -import org.utbot.framework.codegen.domain.builtin.injectMocksClassId -import org.utbot.framework.codegen.domain.builtin.mockClassId -import org.utbot.framework.codegen.domain.builtin.spyClassId -import org.utbot.framework.codegen.domain.context.CgContext -import org.utbot.framework.codegen.domain.context.CgContextOwner -import org.utbot.framework.codegen.domain.models.CgValue -import org.utbot.framework.codegen.domain.models.CgVariable -import org.utbot.framework.codegen.services.framework.SpyFrameworkManager -import org.utbot.framework.codegen.tree.MockitoInjectionUtils.canBeInjectedByTypeInto -import org.utbot.framework.plugin.api.ClassId -import org.utbot.framework.plugin.api.UtAssembleModel -import org.utbot.framework.plugin.api.UtCompositeModel -import org.utbot.framework.plugin.api.UtModel -import org.utbot.framework.plugin.api.UtModelWithCompositeOrigin -import org.utbot.framework.plugin.api.UtSpringEntityManagerModel -import org.utbot.framework.plugin.api.isMockModel -import org.utbot.framework.plugin.api.canBeSpied -import org.utbot.framework.plugin.api.util.SpringModelUtils.autowiredClassId -import org.utbot.framework.plugin.api.util.SpringModelUtils.getBeanNameOrNull -import org.utbot.framework.plugin.api.util.SpringModelUtils.isAutowiredFromContext -import org.utbot.framework.plugin.api.util.SpringModelUtils.persistenceContextClassIds -import org.utbot.framework.plugin.api.util.allDeclaredFieldIds -import org.utbot.framework.plugin.api.util.isSubtypeOf - -interface CgClassFieldManager : CgContextOwner { - - val annotationType: ClassId - - fun constructFieldsForVariable(model: UtModel, variable: CgValue) - - fun fieldWithAnnotationIsRequired(classId: ClassId): Boolean - - fun constructBaseVarName(model: UtModel): String? -} - -abstract class CgAbstractClassFieldManager(context: CgContext) : - CgClassFieldManager, - CgContextOwner by context { - - val variableConstructor: CgSpringVariableConstructor by lazy { - CgComponents.getVariableConstructorBy(context) as CgSpringVariableConstructor - } - - private val nameGenerator = CgComponents.getNameGeneratorBy(context) - - fun findCgValueByModel(model: UtModel, setOfModels: Set?): CgValue? { - val key = setOfModels?.find { it == model.wrap() } ?: return null - return valueByUtModelWrapper[key] - } - - override fun constructBaseVarName(model: UtModel): String? = nameGenerator.nameFrom(model.classId) -} - -class CgInjectingMocksFieldsManager(val context: CgContext) : CgAbstractClassFieldManager(context) { - - override val annotationType = injectMocksClassId - - override fun constructFieldsForVariable(model: UtModel, variable: CgValue) { - val modelFields = when (model) { - is UtCompositeModel -> model.fields - is UtModelWithCompositeOrigin -> model.origin?.fields - else -> null - } - - modelFields?.forEach { (fieldId, fieldModel) -> - // creating variables for modelVariable fields - val variableForField = variableConstructor.getOrCreateVariable(fieldModel) - - // is variable mocked by @Mock annotation - val isMocked = findCgValueByModel(fieldModel, variableConstructor.annotatedModelGroups[mockClassId]) != null - - // is variable spied by @Spy annotation - val isSpied = findCgValueByModel(fieldModel, variableConstructor.annotatedModelGroups[spyClassId]) != null - - // If field model is a mock model and is mocked by @Mock annotation in classFields or is spied by @Spy annotation, - // it is set in the connected with instance under test automatically via @InjectMocks. - // Otherwise, we need to set this field manually. - if ((!fieldModel.isMockModel() || !isMocked) && !isSpied) { - variableConstructor.setFieldValue(variable, fieldId, variableForField) - } - } - } - - override fun fieldWithAnnotationIsRequired(classId: ClassId): Boolean = true -} - -class CgMockedFieldsManager(context: CgContext) : CgAbstractClassFieldManager(context) { - - private val mockFrameworkManager = CgComponents.getMockFrameworkManagerBy(context) - - override val annotationType = mockClassId - - override fun constructFieldsForVariable(model: UtModel, variable: CgValue) { - if (!model.isMockModel()) { - error("$model does not represent a mock") - } - - mockFrameworkManager.createMockForVariable( - model as UtCompositeModel, - variable as CgVariable, - ) - - for ((fieldId, fieldModel) in model.fields) { - val variableForField = variableConstructor.getOrCreateVariable(fieldModel) - variableConstructor.setFieldValue(variable, fieldId, variableForField) - } - } - - override fun fieldWithAnnotationIsRequired(classId: ClassId): Boolean = - classId.canBeInjectedByTypeInto(classUnderTest) -} - -class CgSpiedFieldsManager(context: CgContext) : CgAbstractClassFieldManager(context) { - - private val spyFrameworkManager = SpyFrameworkManager(context) - - override val annotationType = spyClassId - - override fun constructFieldsForVariable(model: UtModel, variable: CgValue) { - if (!model.canBeSpied()) { - error("$model does not represent a spy") - } - spyFrameworkManager.spyForVariable( - model as UtAssembleModel, - ) - } - - override fun fieldWithAnnotationIsRequired(classId: ClassId): Boolean = - classId.canBeInjectedByTypeInto(classUnderTest) - - override fun constructBaseVarName(model: UtModel): String = super.constructBaseVarName(model) + "Spy" -} - -class CgAutowiredFieldsManager(context: CgContext) : CgAbstractClassFieldManager(context) { - - override val annotationType = autowiredClassId - - override fun constructFieldsForVariable(model: UtModel, variable: CgValue) { - when { - model.isAutowiredFromContext() -> { - variableConstructor.constructAssembleForVariable(model as UtAssembleModel) - } - - else -> error("Trying to autowire model $model but it is not appropriate") - } - } - - override fun constructBaseVarName(model: UtModel): String? = model.getBeanNameOrNull() - - override fun fieldWithAnnotationIsRequired(classId: ClassId): Boolean = true -} - -class CgPersistenceContextFieldsManager private constructor( - context: CgContext, - override val annotationType: ClassId, -) : CgAbstractClassFieldManager(context) { - companion object { - fun createIfPossible(context: CgContext): CgPersistenceContextFieldsManager? = persistenceContextClassIds.firstOrNull() - ?.let { persistenceContextClassId -> CgPersistenceContextFieldsManager(context, persistenceContextClassId) } - } - - override fun constructFieldsForVariable(model: UtModel, variable: CgValue) { - return when(model) { - is UtSpringEntityManagerModel -> {} - else -> error("Trying to use @PersistenceContext for model $model but it is not appropriate") - } - } - - override fun fieldWithAnnotationIsRequired(classId: ClassId): Boolean = true -} - -class ClassFieldManagerFacade(context: CgContext) : CgContextOwner by context { - - private val injectingMocksFieldsManager = CgInjectingMocksFieldsManager(context) - private val mockedFieldsManager = CgMockedFieldsManager(context) - private val spiedFieldsManager = CgSpiedFieldsManager(context) - private val autowiredFieldsManager = CgAutowiredFieldsManager(context) - private val persistenceContextFieldsManager = CgPersistenceContextFieldsManager.createIfPossible(context) - - fun constructVariableForField( - model: UtModel, - ): CgValue? { - val annotationManagers = listOfNotNull( - injectingMocksFieldsManager, - mockedFieldsManager, - spiedFieldsManager, - autowiredFieldsManager, - persistenceContextFieldsManager, - ) - - annotationManagers.forEach { manager -> - val annotatedModelGroups = manager.variableConstructor.annotatedModelGroups - - val alreadyCreatedVariable = manager.findCgValueByModel(model, annotatedModelGroups[manager.annotationType]) - - if (alreadyCreatedVariable != null) { - manager.constructFieldsForVariable(model, alreadyCreatedVariable) - return alreadyCreatedVariable - } - } - - return null - } -} - -object MockitoInjectionUtils { - /* - * If count of fields of the same type is 1, then we mock/spy variable by @Mock/@Spy annotation, - * otherwise we will create this variable by simple variable constructor. - */ - fun ClassId.canBeInjectedByTypeInto(classToInjectInto: ClassId): Boolean = - classToInjectInto.allDeclaredFieldIds.filter { isSubtypeOf(it.type) }.toList().size == 1 -} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSpringIntegrationTestClassConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSpringIntegrationTestClassConstructor.kt index eb1dc1a60e..2208a7744e 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSpringIntegrationTestClassConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSpringIntegrationTestClassConstructor.kt @@ -10,7 +10,8 @@ import org.utbot.framework.codegen.domain.models.* import org.utbot.framework.codegen.domain.models.AnnotationTarget.* import org.utbot.framework.codegen.domain.models.CgTestMethodType.FAILING import org.utbot.framework.codegen.domain.models.CgTestMethodType.SUCCESSFUL -import org.utbot.framework.codegen.domain.models.SpringTestClassModel +import org.utbot.framework.codegen.tree.fieldmanager.CgAutowiredFieldsManager +import org.utbot.framework.codegen.tree.fieldmanager.CgPersistenceContextFieldsManager import org.utbot.framework.codegen.util.escapeControlChars import org.utbot.framework.codegen.util.resolve import org.utbot.framework.plugin.api.ClassId @@ -51,21 +52,18 @@ class CgSpringIntegrationTestClassConstructor( private val logger = KotlinLogging.logger {} } - override fun constructTestClass(testClassModel: SpringTestClassModel): CgClass { + override fun constructTestClass(testClassModel: SimpleTestClassModel): CgClass { addNecessarySpringSpecificAnnotations(testClassModel) return super.constructTestClass(testClassModel) } - override fun constructClassFields(testClassModel: SpringTestClassModel): List { - return constructFieldsWithAnnotation( - autowiredFieldManager, - testClassModel.springSpecificInformation.autowiredFromContextModels - ) + persistenceContextFieldsManager?.let { persistenceContextFieldsManager -> - constructFieldsWithAnnotation( - persistenceContextFieldsManager, - testClassModel.springSpecificInformation.entityManagerModels - ) - }.orEmpty() + override fun constructClassFields(testClassModel: SimpleTestClassModel): List { + val autowiredFields = autowiredFieldManager.createFieldDeclarations(testClassModel) + val persistentContextFields = persistenceContextFieldsManager + ?.let { fieldsManager -> fieldsManager.createFieldDeclarations(testClassModel) } + .orEmpty() + + return autowiredFields + persistentContextFields } override fun constructAdditionalTestMethods() = @@ -113,7 +111,7 @@ class CgSpringIntegrationTestClassConstructor( .map { it.escapeControlChars() } ) - private fun addNecessarySpringSpecificAnnotations(testClassModel: SpringTestClassModel) { + private fun addNecessarySpringSpecificAnnotations(testClassModel: SimpleTestClassModel) { val isSpringBootTestAccessible = utContext.classLoader.tryLoadClass(springBootTestClassId.name) != null if (isSpringBootTestAccessible) { addAnnotation(springBootTestClassId, Class) @@ -205,8 +203,9 @@ class CgSpringIntegrationTestClassConstructor( if (utContext.classLoader.tryLoadClass(repositoryClassId.name) != null) addAnnotation(autoConfigureTestDbClassId, Class) - if (mockMvcClassId in testClassModel.springSpecificInformation.autowiredFromContextModels) - addAnnotation(SpringModelUtils.autoConfigureMockMvcClassId, Class) + //TODO: revert a check that this annotation is really required + addAnnotation(SpringModelUtils.autoConfigureMockMvcClassId, Class) + if (utContext.classLoader.tryLoadClass(withMockUserClassId.name) != null) addAnnotation(withMockUserClassId, Class) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSpringUnitTestClassConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSpringUnitTestClassConstructor.kt index 0ec075f385..172765f177 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSpringUnitTestClassConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSpringUnitTestClassConstructor.kt @@ -1,8 +1,6 @@ package org.utbot.framework.codegen.tree import org.utbot.framework.codegen.domain.builtin.closeMethodId -import org.utbot.framework.codegen.domain.builtin.injectMocksClassId -import org.utbot.framework.codegen.domain.builtin.mockClassId import org.utbot.framework.codegen.domain.builtin.openMocksMethodId import org.utbot.framework.codegen.domain.builtin.clearMethodId import org.utbot.framework.codegen.domain.context.CgContext @@ -11,11 +9,13 @@ import org.utbot.framework.codegen.domain.models.CgDeclaration import org.utbot.framework.codegen.domain.models.CgFieldDeclaration import org.utbot.framework.codegen.domain.models.CgMethodCall import org.utbot.framework.codegen.domain.models.CgMethodsCluster -import org.utbot.framework.codegen.domain.models.CgSimpleRegion import org.utbot.framework.codegen.domain.models.CgStatementExecutableCall import org.utbot.framework.codegen.domain.models.CgValue import org.utbot.framework.codegen.domain.models.CgVariable -import org.utbot.framework.codegen.domain.models.SpringTestClassModel +import org.utbot.framework.codegen.domain.models.SimpleTestClassModel +import org.utbot.framework.codegen.tree.fieldmanager.CgInjectingMocksFieldsManager +import org.utbot.framework.codegen.tree.fieldmanager.CgMockedFieldsManager +import org.utbot.framework.codegen.tree.fieldmanager.CgSpiedFieldsManager import org.utbot.framework.plugin.api.UtCompositeModel import org.utbot.framework.plugin.api.util.id import org.utbot.framework.plugin.api.util.jClass @@ -31,17 +31,14 @@ class CgSpringUnitTestClassConstructor(context: CgContext) : CgAbstractSpringTes private val mocksFieldsManager = CgMockedFieldsManager(context) private val spiesFieldsManager = CgSpiedFieldsManager(context) - override fun constructClassFields(testClassModel: SpringTestClassModel): List { + override fun constructClassFields(testClassModel: SimpleTestClassModel): List { val fields = mutableListOf() - val thisInstances = testClassModel.springSpecificInformation.thisInstanceModels - val mocks = testClassModel.springSpecificInformation.thisInstanceDependentMocks - val spies = testClassModel.springSpecificInformation.thisInstanceDependentSpies - val spiesFields = constructFieldsWithAnnotation(spiesFieldsManager, spies) - val mockedFields = constructFieldsWithAnnotation(mocksFieldsManager, mocks) + val spiesFields = spiesFieldsManager.createFieldDeclarations(testClassModel) + val mockedFields = mocksFieldsManager.createFieldDeclarations(testClassModel) if ((spiesFields + mockedFields).isNotEmpty()) { - val injectingMocksFields = constructFieldsWithAnnotation(injectingMocksFieldsManager, thisInstances) + val injectingMocksFields = injectingMocksFieldsManager.createFieldDeclarations(testClassModel) fields += injectingMocksFields fields += mockedFields diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSpringVariableConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSpringVariableConstructor.kt index 0b1333169f..ac4649ef8a 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSpringVariableConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSpringVariableConstructor.kt @@ -1,18 +1,16 @@ package org.utbot.framework.codegen.tree -import org.utbot.framework.codegen.domain.UtModelWrapper import org.utbot.framework.codegen.domain.context.CgContext import org.utbot.framework.codegen.domain.models.CgLiteral import org.utbot.framework.codegen.domain.models.CgValue import org.utbot.framework.codegen.domain.models.CgVariable -import org.utbot.framework.plugin.api.ClassId +import org.utbot.framework.codegen.tree.fieldmanager.ClassFieldManagerFacade import org.utbot.framework.plugin.api.UtModel import org.utbot.framework.plugin.api.UtSpringContextModel import org.utbot.framework.plugin.api.UtSpringEntityManagerModel import org.utbot.framework.plugin.api.util.stringClassId class CgSpringVariableConstructor(context: CgContext) : CgVariableConstructor(context) { - val annotatedModelGroups: MutableMap> = mutableMapOf() private val fieldManagerFacade = ClassFieldManagerFacade(context) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/CgAbstractClassFieldManager.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/CgAbstractClassFieldManager.kt new file mode 100644 index 0000000000..2e992be80b --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/CgAbstractClassFieldManager.kt @@ -0,0 +1,87 @@ +package org.utbot.framework.codegen.tree.fieldmanager + +import org.utbot.framework.codegen.domain.UtModelWrapper +import org.utbot.framework.codegen.domain.context.CgContext +import org.utbot.framework.codegen.domain.context.CgContextOwner +import org.utbot.framework.codegen.domain.models.AnnotationTarget +import org.utbot.framework.codegen.domain.models.CgDeclaration +import org.utbot.framework.codegen.domain.models.CgFieldDeclaration +import org.utbot.framework.codegen.domain.models.CgValue +import org.utbot.framework.codegen.domain.models.CgVariable +import org.utbot.framework.codegen.domain.models.builders.TypedModelWrappers +import org.utbot.framework.codegen.tree.CgComponents +import org.utbot.framework.codegen.tree.CgSpringVariableConstructor +import org.utbot.framework.plugin.api.ClassId +import org.utbot.framework.plugin.api.UtModel + +abstract class CgAbstractClassFieldManager(context: CgContext) : + CgClassFieldManager, + CgContextOwner by context { + + protected val annotatedModelGroups: MutableMap> = mutableMapOf() + protected val modelGroupsProvider = ModelGroupsProvider(context) + + fun getManagedModels(): Set = annotatedModelGroups[annotationType] ?: emptySet() + + fun findCgValueByModel(model: UtModel, setOfModels: Set?): CgValue? { + val key = setOfModels?.find { it == model.wrap() } ?: return null + return valueByUtModelWrapper[key] + } + + protected abstract fun fieldWithAnnotationIsRequired(classId: ClassId): Boolean + + protected fun constructFieldsWithAnnotation(modelWrappers: Set): List { + val groupedModelsByClassId = modelWrappers.groupByClassId() + val annotation = statementConstructor.addAnnotation(annotationType, AnnotationTarget.Field) + + val constructedDeclarations = mutableListOf() + for ((classId, modelWrappers) in groupedModelsByClassId) { + + val modelWrapper = modelWrappers.firstOrNull() ?: continue + val model = modelWrapper.model + + val fieldWithAnnotationIsRequired = fieldWithAnnotationIsRequired(model.classId) + if (!fieldWithAnnotationIsRequired) { + continue + } + + val baseVarName = constructBaseVarName(model) + + val createdVariable = variableConstructor.getOrCreateVariable(model, baseVarName) as? CgVariable + ?: error("`CgVariable` cannot be constructed from a $model model") + + val declaration = CgDeclaration(classId, variableName = createdVariable.name, initializer = null) + + constructedDeclarations += CgFieldDeclaration( + ownerClassId = currentTestClass, + declaration, + annotation + ) + + modelWrappers.forEach { modelWrapper -> + valueByUtModelWrapper[modelWrapper] = createdVariable + annotatedModelGroups.getOrPut(annotationType) { mutableSetOf() } += modelWrapper + } + } + + return constructedDeclarations + } + + protected open fun constructBaseVarName(model: UtModel): String? = nameGenerator.nameFrom(model.classId) + + private fun Set.groupByClassId(): TypedModelWrappers { + val classModels = mutableMapOf>() + + for (modelGroup in this.groupBy { it.model.classId }) { + classModels[modelGroup.key] = modelGroup.value.toSet() + } + + return classModels + } + + protected val variableConstructor: CgSpringVariableConstructor by lazy { + CgComponents.getVariableConstructorBy(context) as CgSpringVariableConstructor + } + protected val nameGenerator = CgComponents.getNameGeneratorBy(context) + protected val statementConstructor = CgComponents.getStatementConstructorBy(context) +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/CgAutowiredFieldsManager.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/CgAutowiredFieldsManager.kt new file mode 100644 index 0000000000..be9c29e1d6 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/CgAutowiredFieldsManager.kt @@ -0,0 +1,42 @@ +package org.utbot.framework.codegen.tree.fieldmanager + +import org.utbot.framework.codegen.domain.context.CgContext +import org.utbot.framework.codegen.domain.models.CgFieldDeclaration +import org.utbot.framework.codegen.domain.models.CgValue +import org.utbot.framework.codegen.domain.models.TestClassModel +import org.utbot.framework.plugin.api.ClassId +import org.utbot.framework.plugin.api.UtAssembleModel +import org.utbot.framework.plugin.api.UtModel +import org.utbot.framework.plugin.api.util.SpringModelUtils +import org.utbot.framework.plugin.api.util.SpringModelUtils.getBeanNameOrNull +import org.utbot.framework.plugin.api.util.SpringModelUtils.isAutowiredFromContext + +class CgAutowiredFieldsManager(context: CgContext) : CgAbstractClassFieldManager(context) { + init { + relevantFieldManagers += this + } + + override val annotationType = SpringModelUtils.autowiredClassId + override fun createFieldDeclarations(testClassModel: TestClassModel): List { + val modelsByOrigin = modelGroupsProvider.collectModelsByOrigin(testClassModel) + val autowiredFromContextModels = modelsByOrigin + .stateBeforeDependentModels + .filterTo(HashSet()) { it.model.isAutowiredFromContext() } + + return constructFieldsWithAnnotation(autowiredFromContextModels) + } + + override fun useVariableForModel(model: UtModel, variable: CgValue) { + when { + model.isAutowiredFromContext() -> { + variableConstructor.constructAssembleForVariable(model as UtAssembleModel) + } + + else -> error("Trying to autowire model $model but it is not appropriate") + } + } + + override fun constructBaseVarName(model: UtModel): String? = model.getBeanNameOrNull() + + override fun fieldWithAnnotationIsRequired(classId: ClassId): Boolean = true +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/CgClassFieldManager.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/CgClassFieldManager.kt new file mode 100644 index 0000000000..8212d57d24 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/CgClassFieldManager.kt @@ -0,0 +1,18 @@ +package org.utbot.framework.codegen.tree.fieldmanager + +import org.utbot.framework.codegen.domain.context.CgContextOwner +import org.utbot.framework.codegen.domain.models.CgFieldDeclaration +import org.utbot.framework.codegen.domain.models.CgValue +import org.utbot.framework.codegen.domain.models.TestClassModel +import org.utbot.framework.plugin.api.ClassId +import org.utbot.framework.plugin.api.UtModel + +interface CgClassFieldManager : CgContextOwner { + + val annotationType: ClassId + + fun createFieldDeclarations(testClassModel: TestClassModel): List + + fun useVariableForModel(model: UtModel, variable: CgValue) +} + diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/CgInjectingMocksFieldsManager.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/CgInjectingMocksFieldsManager.kt new file mode 100644 index 0000000000..7ac001e332 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/CgInjectingMocksFieldsManager.kt @@ -0,0 +1,55 @@ +package org.utbot.framework.codegen.tree.fieldmanager + +import org.utbot.framework.codegen.domain.builtin.injectMocksClassId +import org.utbot.framework.codegen.domain.builtin.mockClassId +import org.utbot.framework.codegen.domain.builtin.spyClassId +import org.utbot.framework.codegen.domain.context.CgContext +import org.utbot.framework.codegen.domain.models.CgFieldDeclaration +import org.utbot.framework.codegen.domain.models.CgValue +import org.utbot.framework.codegen.domain.models.TestClassModel +import org.utbot.framework.plugin.api.ClassId +import org.utbot.framework.plugin.api.UtCompositeModel +import org.utbot.framework.plugin.api.UtModel +import org.utbot.framework.plugin.api.UtModelWithCompositeOrigin +import org.utbot.framework.plugin.api.isMockModel + +class CgInjectingMocksFieldsManager(val context: CgContext) : CgAbstractClassFieldManager(context) { + init { + relevantFieldManagers += this + } + + override val annotationType = injectMocksClassId + + override fun createFieldDeclarations(testClassModel: TestClassModel): List { + val modelsByOrigin = modelGroupsProvider.collectModelsByOrigin(testClassModel) + return constructFieldsWithAnnotation(modelsByOrigin.thisInstanceModels) + } + + override fun useVariableForModel(model: UtModel, variable: CgValue) { + val modelFields = when (model) { + is UtCompositeModel -> model.fields + is UtModelWithCompositeOrigin -> model.origin?.fields + else -> null + } + + modelFields?.forEach { (fieldId, fieldModel) -> + // creating variables for modelVariable fields + val variableForField = variableConstructor.getOrCreateVariable(fieldModel) + + // is variable mocked by @Mock annotation + val isMocked = findCgValueByModel(fieldModel, annotatedModelGroups[mockClassId]) != null + + // is variable spied by @Spy annotation + val isSpied = findCgValueByModel(fieldModel, annotatedModelGroups[spyClassId]) != null + + // If field model is a mock model and is mocked by @Mock annotation in classFields or is spied by @Spy annotation, + // it is set in the connected with instance under test automatically via @InjectMocks. + // Otherwise, we need to set this field manually. + if ((!fieldModel.isMockModel() || !isMocked) && !isSpied) { + variableConstructor.setFieldValue(variable, fieldId, variableForField) + } + } + } + + override fun fieldWithAnnotationIsRequired(classId: ClassId): Boolean = true +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/CgMockedFieldsManager.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/CgMockedFieldsManager.kt new file mode 100644 index 0000000000..b9a3e81dc5 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/CgMockedFieldsManager.kt @@ -0,0 +1,53 @@ +package org.utbot.framework.codegen.tree.fieldmanager + +import org.utbot.framework.codegen.domain.builtin.mockClassId +import org.utbot.framework.codegen.domain.context.CgContext +import org.utbot.framework.codegen.domain.models.CgFieldDeclaration +import org.utbot.framework.codegen.domain.models.CgValue +import org.utbot.framework.codegen.domain.models.CgVariable +import org.utbot.framework.codegen.domain.models.TestClassModel +import org.utbot.framework.codegen.tree.CgComponents +import org.utbot.framework.codegen.tree.fieldmanager.MockitoInjectionUtils.canBeInjectedByTypeInto +import org.utbot.framework.plugin.api.ClassId +import org.utbot.framework.plugin.api.UtCompositeModel +import org.utbot.framework.plugin.api.UtModel +import org.utbot.framework.plugin.api.isMockModel + +class CgMockedFieldsManager(context: CgContext) : CgAbstractClassFieldManager(context) { + init { + relevantFieldManagers += this + } + + override val annotationType = mockClassId + override fun createFieldDeclarations(testClassModel: TestClassModel): List { + val modelsByOrigin = modelGroupsProvider.collectModelsByOrigin(testClassModel) + + val dependentMockModels = + modelsByOrigin.thisInstanceDependentModels + .filterTo(mutableSetOf()) { cgModel -> + cgModel.model.isMockModel() && cgModel !in modelsByOrigin.thisInstanceModels + } + + return constructFieldsWithAnnotation(dependentMockModels) + } + + private val mockFrameworkManager = CgComponents.getMockFrameworkManagerBy(context) + override fun useVariableForModel(model: UtModel, variable: CgValue) { + if (!model.isMockModel()) { + error("$model does not represent a mock") + } + + mockFrameworkManager.createMockForVariable( + model as UtCompositeModel, + variable as CgVariable, + ) + + for ((fieldId, fieldModel) in model.fields) { + val variableForField = variableConstructor.getOrCreateVariable(fieldModel) + variableConstructor.setFieldValue(variable, fieldId, variableForField) + } + } + + override fun fieldWithAnnotationIsRequired(classId: ClassId): Boolean = + classId.canBeInjectedByTypeInto(classUnderTest) +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/CgPersistenceContextFieldsManager.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/CgPersistenceContextFieldsManager.kt new file mode 100644 index 0000000000..83bd944dad --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/CgPersistenceContextFieldsManager.kt @@ -0,0 +1,43 @@ +package org.utbot.framework.codegen.tree.fieldmanager + +import org.utbot.framework.codegen.domain.context.CgContext +import org.utbot.framework.codegen.domain.models.CgFieldDeclaration +import org.utbot.framework.codegen.domain.models.CgValue +import org.utbot.framework.codegen.domain.models.TestClassModel +import org.utbot.framework.plugin.api.ClassId +import org.utbot.framework.plugin.api.UtModel +import org.utbot.framework.plugin.api.UtSpringEntityManagerModel +import org.utbot.framework.plugin.api.util.SpringModelUtils + +class CgPersistenceContextFieldsManager private constructor( + context: CgContext, + override val annotationType: ClassId, +) : CgAbstractClassFieldManager(context) { + + init { + relevantFieldManagers += this + } + + companion object { + fun createIfPossible(context: CgContext): CgPersistenceContextFieldsManager? = SpringModelUtils.persistenceContextClassIds.firstOrNull() + ?.let { persistenceContextClassId -> CgPersistenceContextFieldsManager(context, persistenceContextClassId) } + } + + override fun createFieldDeclarations(testClassModel: TestClassModel): List { + val modelsByOrigin = modelGroupsProvider.collectModelsByOrigin(testClassModel) + val entityManagerModels = modelsByOrigin + .stateBeforeDependentModels + .filterTo(HashSet()) { it.model is UtSpringEntityManagerModel } + + return constructFieldsWithAnnotation(entityManagerModels) + } + + override fun useVariableForModel(model: UtModel, variable: CgValue) { + return when(model) { + is UtSpringEntityManagerModel -> {} + else -> error("Trying to use @PersistenceContext for model $model but it is not appropriate") + } + } + + override fun fieldWithAnnotationIsRequired(classId: ClassId): Boolean = true +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/CgSpiedFieldsManager.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/CgSpiedFieldsManager.kt new file mode 100644 index 0000000000..4fdab3e496 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/CgSpiedFieldsManager.kt @@ -0,0 +1,60 @@ +package org.utbot.framework.codegen.tree.fieldmanager + +import org.utbot.framework.codegen.domain.builtin.spyClassId +import org.utbot.framework.codegen.domain.context.CgContext +import org.utbot.framework.codegen.domain.models.CgFieldDeclaration +import org.utbot.framework.codegen.domain.models.CgValue +import org.utbot.framework.codegen.domain.models.TestClassModel +import org.utbot.framework.codegen.services.framework.SpyFrameworkManager +import org.utbot.framework.codegen.tree.fieldmanager.MockitoInjectionUtils.canBeInjectedByTypeInto +import org.utbot.framework.plugin.api.ClassId +import org.utbot.framework.plugin.api.UtAssembleModel +import org.utbot.framework.plugin.api.UtModel +import org.utbot.framework.plugin.api.canBeSpied +import org.utbot.framework.plugin.api.isMockModel + +class CgSpiedFieldsManager(context: CgContext) : CgAbstractClassFieldManager(context) { + + init { + relevantFieldManagers += this + } + + override val annotationType = spyClassId + + override fun createFieldDeclarations(testClassModel: TestClassModel): List { + val modelsByOrigin = modelGroupsProvider.collectModelsByOrigin(testClassModel) + + val dependentMockModels = + modelsByOrigin.thisInstanceDependentModels + .filterTo(mutableSetOf()) { cgModel -> + cgModel.model.isMockModel() && cgModel !in modelsByOrigin.thisInstanceModels + } + + val dependentSpyModels = + modelsByOrigin.thisInstanceDependentModels + .filterTo(mutableSetOf()) { cgModel -> + cgModel.model.canBeSpied() && + cgModel !in modelsByOrigin.thisInstanceModels && + cgModel !in dependentMockModels + } + + return constructFieldsWithAnnotation(dependentSpyModels) + } + + + private val spyFrameworkManager = SpyFrameworkManager(context) + + override fun useVariableForModel(model: UtModel, variable: CgValue) { + if (!model.canBeSpied()) { + error("$model does not represent a spy") + } + spyFrameworkManager.spyForVariable( + model as UtAssembleModel, + ) + } + + override fun fieldWithAnnotationIsRequired(classId: ClassId): Boolean = + classId.canBeInjectedByTypeInto(classUnderTest) + + override fun constructBaseVarName(model: UtModel): String = super.constructBaseVarName(model) + "Spy" +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/ClassFieldManagerFacade.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/ClassFieldManagerFacade.kt new file mode 100644 index 0000000000..fcc4674a9c --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/ClassFieldManagerFacade.kt @@ -0,0 +1,35 @@ +package org.utbot.framework.codegen.tree.fieldmanager + +import org.utbot.framework.codegen.domain.UtModelWrapper +import org.utbot.framework.codegen.domain.context.CgContext +import org.utbot.framework.codegen.domain.context.CgContextOwner +import org.utbot.framework.codegen.domain.models.CgValue +import org.utbot.framework.plugin.api.UtModel +import org.utbot.framework.plugin.api.UtSpringContextModel + +class ClassFieldManagerFacade(context: CgContext) : CgContextOwner by context { + + fun constructVariableForField(model: UtModel): CgValue? { + relevantFieldManagers.forEach { manager -> + val managedModels = manager.getManagedModels() + + val alreadyCreatedVariable = manager.findCgValueByModel(model, managedModels) + if (alreadyCreatedVariable != null) { + manager.useVariableForModel(model, alreadyCreatedVariable) + return alreadyCreatedVariable + } + } + + return null + } + + fun findTrustedModels(): List { + val trustedModels = mutableListOf(UtSpringContextModel.wrap()) + + relevantFieldManagers.forEach { manager -> + trustedModels += manager.getManagedModels() + } + + return trustedModels + } +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/MockitoInjectionUtils.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/MockitoInjectionUtils.kt new file mode 100644 index 0000000000..5f5176257b --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/MockitoInjectionUtils.kt @@ -0,0 +1,14 @@ +package org.utbot.framework.codegen.tree.fieldmanager + +import org.utbot.framework.plugin.api.ClassId +import org.utbot.framework.plugin.api.util.allDeclaredFieldIds +import org.utbot.framework.plugin.api.util.isSubtypeOf + +object MockitoInjectionUtils { + /* + * If count of fields of the same type is 1, then we mock/spy variable by @Mock/@Spy annotation, + * otherwise we will create this variable by simple variable constructor. + */ + fun ClassId.canBeInjectedByTypeInto(classToInjectInto: ClassId): Boolean = + classToInjectInto.allDeclaredFieldIds.filter { isSubtypeOf(it.type) }.toList().size == 1 +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/builders/SpringTestClassModelBuilder.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/ModelGroupsProvider.kt similarity index 61% rename from utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/builders/SpringTestClassModelBuilder.kt rename to utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/ModelGroupsProvider.kt index 6386ad2dea..7b42558f12 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/builders/SpringTestClassModelBuilder.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/ModelGroupsProvider.kt @@ -1,54 +1,43 @@ -package org.utbot.framework.codegen.domain.models.builders +package org.utbot.framework.codegen.tree.fieldmanager import org.utbot.framework.codegen.domain.UtModelWrapper import org.utbot.framework.codegen.domain.context.CgContext import org.utbot.framework.codegen.domain.context.CgContextOwner -import org.utbot.framework.codegen.domain.models.CgMethodTestSet -import org.utbot.framework.codegen.domain.models.SpringSpecificInformation -import org.utbot.framework.codegen.domain.models.SpringTestClassModel -import org.utbot.framework.plugin.api.ClassId +import org.utbot.framework.codegen.domain.models.TestClassModel import org.utbot.framework.plugin.api.UtArrayModel import org.utbot.framework.plugin.api.UtAssembleModel import org.utbot.framework.plugin.api.UtClassRefModel import org.utbot.framework.plugin.api.UtCompositeModel import org.utbot.framework.plugin.api.UtCustomModel +import org.utbot.framework.plugin.api.UtDirectSetFieldModel import org.utbot.framework.plugin.api.UtEnumConstantModel import org.utbot.framework.plugin.api.UtLambdaModel import org.utbot.framework.plugin.api.UtModel import org.utbot.framework.plugin.api.UtNullModel import org.utbot.framework.plugin.api.UtPrimitiveModel -import org.utbot.framework.plugin.api.UtSpringEntityManagerModel -import org.utbot.framework.plugin.api.UtVoidModel -import org.utbot.framework.plugin.api.isMockModel import org.utbot.framework.plugin.api.UtStatementCallModel -import org.utbot.framework.plugin.api.UtDirectSetFieldModel -import org.utbot.framework.plugin.api.util.SpringModelUtils.isAutowiredFromContext -import org.utbot.framework.plugin.api.canBeSpied +import org.utbot.framework.plugin.api.UtVoidModel -typealias TypedModelWrappers = Map> +data class ModelsByOrigin( + val thisInstanceModels: Set, + val thisInstanceDependentModels: Set, + val stateBeforeDependentModels: Set, +) -class SpringTestClassModelBuilder(val context: CgContext) : - TestClassModelBuilder(), - CgContextOwner by context { +class ModelGroupsProvider(val context: CgContext): CgContextOwner by context { - override fun createTestClassModel(classUnderTest: ClassId, testSets: List): SpringTestClassModel { - val baseModel = SimpleTestClassModelBuilder(context).createTestClassModel(classUnderTest, testSets) - val springSpecificInformation = collectSpecificModelsForClassVariables(testSets) + private val modelsCache = mutableMapOf() - return SpringTestClassModel( - classUnderTest = baseModel.classUnderTest, - methodTestSets = baseModel.methodTestSets, - nestedClasses = baseModel.nestedClasses, - springSpecificInformation = springSpecificInformation - ) - } + fun collectModelsByOrigin(testClassModel: TestClassModel): ModelsByOrigin { + if (testClassModel in modelsCache) { + return modelsCache[testClassModel]!! + } - private fun collectSpecificModelsForClassVariables(testSets: List): SpringSpecificInformation { val thisInstanceModels = mutableSetOf() val thisInstancesDependentModels = mutableSetOf() val stateBeforeDependentModels = mutableSetOf() - for ((testSetIndex, testSet) in testSets.withIndex()) { + for ((testSetIndex, testSet) in testClassModel.methodTestSets.withIndex()) { withTestSetIdScope(testSetIndex) { for ((executionIndex, execution) in testSet.executions.withIndex()) { withExecutionIdScope(executionIndex) { @@ -70,48 +59,10 @@ class SpringTestClassModelBuilder(val context: CgContext) : } } - val dependentMockModels = - thisInstancesDependentModels - .filterTo(mutableSetOf()) { cgModel -> - cgModel.model.isMockModel() && cgModel !in thisInstanceModels - } - - val dependentSpyModels = - thisInstancesDependentModels - .filterTo(mutableSetOf()) { cgModel -> - cgModel.model.canBeSpied() && cgModel !in thisInstanceModels && cgModel !in dependentMockModels - } - - val autowiredFromContextModels = - stateBeforeDependentModels.filterTo(HashSet()) { it.model.isAutowiredFromContext() } - - val entityManagerModels = - stateBeforeDependentModels.filterTo(HashSet()) { it.model is UtSpringEntityManagerModel } - - return SpringSpecificInformation( - thisInstanceModels.groupByClassId(), - dependentMockModels.groupByClassId(), - dependentSpyModels.groupByClassId(), - autowiredFromContextModels.groupByClassId(), - entityManagerModels.groupByClassId(), - ) - } - - private fun collectRecursively(model: UtModel): Set { - val allDependentModels = mutableSetOf() - - collectRecursively(model, allDependentModels) - - return allDependentModels - } + val modelsByOrigin = ModelsByOrigin(thisInstanceModels, thisInstancesDependentModels, stateBeforeDependentModels) + modelsCache[testClassModel] = modelsByOrigin - private fun collectRecursively(model: UtModel, allDependentModels: MutableSet){ - if(!allDependentModels.add(model.wrap())){ - return - } - collectImmediateDependentModels(model, skipModificationChains = false).forEach { - collectRecursively(it.model, allDependentModels) - } + return modelsByOrigin } private fun collectImmediateDependentModels(model: UtModel, skipModificationChains: Boolean): Set { @@ -158,15 +109,20 @@ class SpringTestClassModelBuilder(val context: CgContext) : return dependentModels } + private fun collectRecursively(model: UtModel): Set { + val allDependentModels = mutableSetOf() - private fun Set.groupByClassId(): TypedModelWrappers { - val classModels = mutableMapOf>() - - for (modelGroup in this.groupBy { it.model.classId }) { - classModels[modelGroup.key] = modelGroup.value.toSet() - } + collectRecursively(model, allDependentModels) - return classModels + return allDependentModels } + private fun collectRecursively(model: UtModel, allDependentModels: MutableSet){ + if(!allDependentModels.add(model.wrap())){ + return + } + collectImmediateDependentModels(model, skipModificationChains = false).forEach { + collectRecursively(it.model, allDependentModels) + } + } } \ No newline at end of file