From d685bb6a61f35bf0b2b36748858cfebba611714c Mon Sep 17 00:00:00 2001 From: Egor Kulikov Date: Mon, 7 Aug 2023 16:17:27 +0300 Subject: [PATCH 1/3] First step --- .../codegen/domain/models/TestClassModel.kt | 17 -- .../builders/SimpleTestClassModelBuilder.kt | 6 +- .../codegen/generator/CodeGenerator.kt | 2 +- .../codegen/generator/SpringCodeGenerator.kt | 4 +- .../CgAbstractSpringTestClassConstructor.kt | 57 +----- ...CgSpringIntegrationTestClassConstructor.kt | 29 ++- .../tree/CgSpringUnitTestClassConstructor.kt | 19 +- .../tree/CgSpringVariableConstructor.kt | 1 + .../{ => fieldmanager}/CgClassFieldManager.kt | 170 +++++++++++++----- .../fieldmanager/ClassFieldManagerFacade.kt | 40 +++++ .../fieldmanager/FieldManagerUtils.kt} | 106 ++++------- .../fieldmanager/MockitoInjectionUtils.kt | 14 ++ 12 files changed, 242 insertions(+), 223 deletions(-) rename utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/{ => fieldmanager}/CgClassFieldManager.kt (56%) create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/ClassFieldManagerFacade.kt rename utbot-framework/src/main/kotlin/org/utbot/framework/codegen/{domain/models/builders/SpringTestClassModelBuilder.kt => tree/fieldmanager/FieldManagerUtils.kt} (61%) create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/MockitoInjectionUtils.kt 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..0c79166867 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.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 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..672e06b1cd 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 @@ -5,6 +5,7 @@ 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.codegen.tree.fieldmanager.ClassFieldManagerFacade import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.UtModel import org.utbot.framework.plugin.api.UtSpringContextModel 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/fieldmanager/CgClassFieldManager.kt similarity index 56% rename from utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgClassFieldManager.kt rename to utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/CgClassFieldManager.kt index e2c8a3b83f..4651261fc1 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgClassFieldManager.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/CgClassFieldManager.kt @@ -1,4 +1,4 @@ -package org.utbot.framework.codegen.tree +package org.utbot.framework.codegen.tree.fieldmanager import org.utbot.framework.codegen.domain.UtModelWrapper import org.utbot.framework.codegen.domain.builtin.injectMocksClassId @@ -6,29 +6,36 @@ 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.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.TestClassModel +import org.utbot.framework.codegen.domain.models.builders.TypedModelWrappers import org.utbot.framework.codegen.services.framework.SpyFrameworkManager -import org.utbot.framework.codegen.tree.MockitoInjectionUtils.canBeInjectedByTypeInto +import org.utbot.framework.codegen.tree.CgComponents +import org.utbot.framework.codegen.tree.CgSpringVariableConstructor +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.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.isMockModel 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 createFieldDeclarations(testClassModel: TestClassModel): List + fun constructFieldsForVariable(model: UtModel, variable: CgValue) fun fieldWithAnnotationIsRequired(classId: ClassId): Boolean @@ -44,20 +51,80 @@ abstract class CgAbstractClassFieldManager(context: CgContext) : CgComponents.getVariableConstructorBy(context) as CgSpringVariableConstructor } - private val nameGenerator = CgComponents.getNameGeneratorBy(context) + private val statementConstructor = CgComponents.getStatementConstructorBy(context) + + protected val fieldManagerUtils = FieldManagerUtils(context) fun findCgValueByModel(model: UtModel, setOfModels: Set?): CgValue? { val key = setOfModels?.find { it == model.wrap() } ?: return null return valueByUtModelWrapper[key] } + 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 + + variableConstructor.annotatedModelGroups + .getOrPut(annotationType) { mutableSetOf() } += modelWrapper + } + } + + return constructedDeclarations + } + + private fun Set.groupByClassId(): TypedModelWrappers { + val classModels = mutableMapOf>() + + for (modelGroup in this.groupBy { it.model.classId }) { + classModels[modelGroup.key] = modelGroup.value.toSet() + } + + return classModels + } + override fun constructBaseVarName(model: UtModel): String? = nameGenerator.nameFrom(model.classId) + + private val nameGenerator = CgComponents.getNameGeneratorBy(context) } class CgInjectingMocksFieldsManager(val context: CgContext) : CgAbstractClassFieldManager(context) { override val annotationType = injectMocksClassId + override fun createFieldDeclarations(testClassModel: TestClassModel): List { + val modelsByOrigin = fieldManagerUtils.collectModelsByOrigin(testClassModel) + return constructFieldsWithAnnotation(modelsByOrigin.thisInstanceModels) + } + override fun constructFieldsForVariable(model: UtModel, variable: CgValue) { val modelFields = when (model) { is UtCompositeModel -> model.fields @@ -92,6 +159,17 @@ class CgMockedFieldsManager(context: CgContext) : CgAbstractClassFieldManager(co private val mockFrameworkManager = CgComponents.getMockFrameworkManagerBy(context) override val annotationType = mockClassId + override fun createFieldDeclarations(testClassModel: TestClassModel): List { + val modelsByOrigin = fieldManagerUtils.collectModelsByOrigin(testClassModel) + + val dependentMockModels = + modelsByOrigin.thisInstanceDependentModels + .filterTo(mutableSetOf()) { cgModel -> + cgModel.model.isMockModel() && cgModel !in modelsByOrigin.thisInstanceModels + } + + return constructFieldsWithAnnotation(dependentMockModels) + } override fun constructFieldsForVariable(model: UtModel, variable: CgValue) { if (!model.isMockModel()) { @@ -119,6 +197,26 @@ class CgSpiedFieldsManager(context: CgContext) : CgAbstractClassFieldManager(con override val annotationType = spyClassId + override fun createFieldDeclarations(testClassModel: TestClassModel): List { + val modelsByOrigin = fieldManagerUtils.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) + } + override fun constructFieldsForVariable(model: UtModel, variable: CgValue) { if (!model.canBeSpied()) { error("$model does not represent a spy") @@ -138,6 +236,15 @@ class CgAutowiredFieldsManager(context: CgContext) : CgAbstractClassFieldManager override val annotationType = autowiredClassId + override fun createFieldDeclarations(testClassModel: TestClassModel): List { + val modelsByOrigin = fieldManagerUtils.collectModelsByOrigin(testClassModel) + val autowiredFromContextModels = modelsByOrigin + .stateBeforeDependentModels + .filterTo(HashSet()) { it.model.isAutowiredFromContext() } + + return constructFieldsWithAnnotation(autowiredFromContextModels) + } + override fun constructFieldsForVariable(model: UtModel, variable: CgValue) { when { model.isAutowiredFromContext() -> { @@ -162,6 +269,15 @@ class CgPersistenceContextFieldsManager private constructor( ?.let { persistenceContextClassId -> CgPersistenceContextFieldsManager(context, persistenceContextClassId) } } + override fun createFieldDeclarations(testClassModel: TestClassModel): List { + val modelsByOrigin = fieldManagerUtils.collectModelsByOrigin(testClassModel) + val entityManagerModels = modelsByOrigin + .stateBeforeDependentModels + .filterTo(HashSet()) { it.model is UtSpringEntityManagerModel } + + return constructFieldsWithAnnotation(entityManagerModels) + } + override fun constructFieldsForVariable(model: UtModel, variable: CgValue) { return when(model) { is UtSpringEntityManagerModel -> {} @@ -172,45 +288,3 @@ class CgPersistenceContextFieldsManager private constructor( 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/fieldmanager/ClassFieldManagerFacade.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/ClassFieldManagerFacade.kt new file mode 100644 index 0000000000..c58cd35471 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/ClassFieldManagerFacade.kt @@ -0,0 +1,40 @@ +package org.utbot.framework.codegen.tree.fieldmanager + +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 + +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 + } +} \ 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/FieldManagerUtils.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/FieldManagerUtils.kt index 6386ad2dea..9502468a2b 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/FieldManagerUtils.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 FieldManagerUtils(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 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 From 0923facf93d1a69445c7138fe5b784645003d899 Mon Sep 17 00:00:00 2001 From: Egor Kulikov Date: Mon, 7 Aug 2023 16:44:10 +0300 Subject: [PATCH 2/3] Second step --- .../CgAbstractSpringTestClassConstructor.kt | 5 +-- .../tree/CgSpringVariableConstructor.kt | 3 -- .../tree/fieldmanager/CgClassFieldManager.kt | 18 ++++----- .../fieldmanager/ClassFieldManagerFacade.kt | 37 ++++++++++++------- 4 files changed, 34 insertions(+), 29 deletions(-) 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 0c79166867..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 @@ -13,8 +13,8 @@ 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.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 abstract class CgAbstractSpringTestClassConstructor(context: CgContext) : @@ -98,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/CgSpringVariableConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSpringVariableConstructor.kt index 672e06b1cd..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,19 +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.codegen.tree.fieldmanager.ClassFieldManagerFacade -import org.utbot.framework.plugin.api.ClassId 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/CgClassFieldManager.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/CgClassFieldManager.kt index 4651261fc1..b0c46727b6 100644 --- 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 @@ -55,6 +55,8 @@ abstract class CgAbstractClassFieldManager(context: CgContext) : protected val fieldManagerUtils = FieldManagerUtils(context) + val annotatedModelGroups: MutableMap> = mutableMapOf() + fun findCgValueByModel(model: UtModel, setOfModels: Set?): CgValue? { val key = setOfModels?.find { it == model.wrap() } ?: return null return valueByUtModelWrapper[key] @@ -88,14 +90,10 @@ abstract class CgAbstractClassFieldManager(context: CgContext) : annotation ) - modelWrappers - .forEach { modelWrapper -> - - valueByUtModelWrapper[modelWrapper] = createdVariable - - variableConstructor.annotatedModelGroups - .getOrPut(annotationType) { mutableSetOf() } += modelWrapper - } + modelWrappers.forEach { modelWrapper -> + valueByUtModelWrapper[modelWrapper] = createdVariable + annotatedModelGroups.getOrPut(annotationType) { mutableSetOf() } += modelWrapper + } } return constructedDeclarations @@ -137,10 +135,10 @@ class CgInjectingMocksFieldsManager(val context: CgContext) : CgAbstractClassFie val variableForField = variableConstructor.getOrCreateVariable(fieldModel) // is variable mocked by @Mock annotation - val isMocked = findCgValueByModel(fieldModel, variableConstructor.annotatedModelGroups[mockClassId]) != null + val isMocked = findCgValueByModel(fieldModel, annotatedModelGroups[mockClassId]) != null // is variable spied by @Spy annotation - val isSpied = findCgValueByModel(fieldModel, variableConstructor.annotatedModelGroups[spyClassId]) != null + 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. 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 index c58cd35471..bad2c3b7f0 100644 --- 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 @@ -1,9 +1,11 @@ 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 { @@ -13,22 +15,19 @@ class ClassFieldManagerFacade(context: CgContext) : CgContextOwner by 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, - ) + private val annotationManagers = listOfNotNull( + injectingMocksFieldsManager, + mockedFieldsManager, + spiedFieldsManager, + autowiredFieldsManager, + persistenceContextFieldsManager, + ) + fun constructVariableForField(model: UtModel): CgValue? { annotationManagers.forEach { manager -> - val annotatedModelGroups = manager.variableConstructor.annotatedModelGroups - - val alreadyCreatedVariable = manager.findCgValueByModel(model, annotatedModelGroups[manager.annotationType]) + val managedModels = manager.annotatedModelGroups[manager.annotationType] + val alreadyCreatedVariable = manager.findCgValueByModel(model, managedModels) if (alreadyCreatedVariable != null) { manager.constructFieldsForVariable(model, alreadyCreatedVariable) return alreadyCreatedVariable @@ -37,4 +36,16 @@ class ClassFieldManagerFacade(context: CgContext) : CgContextOwner by context { return null } + + fun findTrustedModels(): List { + val trustedModels = mutableListOf() + annotationManagers.forEach { manager -> + val managedModels = manager.annotatedModelGroups[manager.annotationType] + trustedModels += managedModels ?: emptyList() + } + + trustedModels += listOf(UtSpringContextModel.wrap()) + + return trustedModels + } } \ No newline at end of file From 1f3d2c04bfa425f65201a47fb63fa9b9c2713ad4 Mon Sep 17 00:00:00 2001 From: Egor Kulikov Date: Tue, 8 Aug 2023 09:27:06 +0300 Subject: [PATCH 3/3] Next step --- .../codegen/domain/context/CgContext.kt | 10 + .../CgAbstractClassFieldManager.kt | 87 ++++++ .../fieldmanager/CgAutowiredFieldsManager.kt | 42 +++ .../tree/fieldmanager/CgClassFieldManager.kt | 272 +----------------- .../CgInjectingMocksFieldsManager.kt | 55 ++++ .../fieldmanager/CgMockedFieldsManager.kt | 53 ++++ .../CgPersistenceContextFieldsManager.kt | 43 +++ .../tree/fieldmanager/CgSpiedFieldsManager.kt | 60 ++++ .../fieldmanager/ClassFieldManagerFacade.kt | 30 +- ...ManagerUtils.kt => ModelGroupsProvider.kt} | 2 +- 10 files changed, 359 insertions(+), 295 deletions(-) create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/CgAbstractClassFieldManager.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/CgAutowiredFieldsManager.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/CgInjectingMocksFieldsManager.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/CgMockedFieldsManager.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/CgPersistenceContextFieldsManager.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/CgSpiedFieldsManager.kt rename utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/{FieldManagerUtils.kt => ModelGroupsProvider.kt} (98%) 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/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 index b0c46727b6..8212d57d24 100644 --- 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 @@ -1,34 +1,11 @@ package org.utbot.framework.codegen.tree.fieldmanager -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.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.TestClassModel -import org.utbot.framework.codegen.domain.models.builders.TypedModelWrappers -import org.utbot.framework.codegen.services.framework.SpyFrameworkManager -import org.utbot.framework.codegen.tree.CgComponents -import org.utbot.framework.codegen.tree.CgSpringVariableConstructor -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.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.canBeSpied -import org.utbot.framework.plugin.api.isMockModel -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 interface CgClassFieldManager : CgContextOwner { @@ -36,253 +13,6 @@ interface CgClassFieldManager : CgContextOwner { fun createFieldDeclarations(testClassModel: TestClassModel): List - 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 statementConstructor = CgComponents.getStatementConstructorBy(context) - - protected val fieldManagerUtils = FieldManagerUtils(context) - - val annotatedModelGroups: MutableMap> = mutableMapOf() - - fun findCgValueByModel(model: UtModel, setOfModels: Set?): CgValue? { - val key = setOfModels?.find { it == model.wrap() } ?: return null - return valueByUtModelWrapper[key] - } - - 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 - } - - private fun Set.groupByClassId(): TypedModelWrappers { - val classModels = mutableMapOf>() - - for (modelGroup in this.groupBy { it.model.classId }) { - classModels[modelGroup.key] = modelGroup.value.toSet() - } - - return classModels - } - - override fun constructBaseVarName(model: UtModel): String? = nameGenerator.nameFrom(model.classId) - - private val nameGenerator = CgComponents.getNameGeneratorBy(context) -} - -class CgInjectingMocksFieldsManager(val context: CgContext) : CgAbstractClassFieldManager(context) { - - override val annotationType = injectMocksClassId - - override fun createFieldDeclarations(testClassModel: TestClassModel): List { - val modelsByOrigin = fieldManagerUtils.collectModelsByOrigin(testClassModel) - return constructFieldsWithAnnotation(modelsByOrigin.thisInstanceModels) - } - - 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, 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 -} - -class CgMockedFieldsManager(context: CgContext) : CgAbstractClassFieldManager(context) { - - private val mockFrameworkManager = CgComponents.getMockFrameworkManagerBy(context) - - override val annotationType = mockClassId - override fun createFieldDeclarations(testClassModel: TestClassModel): List { - val modelsByOrigin = fieldManagerUtils.collectModelsByOrigin(testClassModel) - - val dependentMockModels = - modelsByOrigin.thisInstanceDependentModels - .filterTo(mutableSetOf()) { cgModel -> - cgModel.model.isMockModel() && cgModel !in modelsByOrigin.thisInstanceModels - } - - return constructFieldsWithAnnotation(dependentMockModels) - } - - 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 createFieldDeclarations(testClassModel: TestClassModel): List { - val modelsByOrigin = fieldManagerUtils.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) - } - - 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 createFieldDeclarations(testClassModel: TestClassModel): List { - val modelsByOrigin = fieldManagerUtils.collectModelsByOrigin(testClassModel) - val autowiredFromContextModels = modelsByOrigin - .stateBeforeDependentModels - .filterTo(HashSet()) { it.model.isAutowiredFromContext() } - - return constructFieldsWithAnnotation(autowiredFromContextModels) - } - - 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 createFieldDeclarations(testClassModel: TestClassModel): List { - val modelsByOrigin = fieldManagerUtils.collectModelsByOrigin(testClassModel) - val entityManagerModels = modelsByOrigin - .stateBeforeDependentModels - .filterTo(HashSet()) { it.model is UtSpringEntityManagerModel } - - return constructFieldsWithAnnotation(entityManagerModels) - } - - 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 + 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 index bad2c3b7f0..fcc4674a9c 100644 --- 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 @@ -9,27 +9,13 @@ import org.utbot.framework.plugin.api.UtSpringContextModel 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) - - private val annotationManagers = listOfNotNull( - injectingMocksFieldsManager, - mockedFieldsManager, - spiedFieldsManager, - autowiredFieldsManager, - persistenceContextFieldsManager, - ) - fun constructVariableForField(model: UtModel): CgValue? { - annotationManagers.forEach { manager -> - val managedModels = manager.annotatedModelGroups[manager.annotationType] + relevantFieldManagers.forEach { manager -> + val managedModels = manager.getManagedModels() val alreadyCreatedVariable = manager.findCgValueByModel(model, managedModels) if (alreadyCreatedVariable != null) { - manager.constructFieldsForVariable(model, alreadyCreatedVariable) + manager.useVariableForModel(model, alreadyCreatedVariable) return alreadyCreatedVariable } } @@ -38,13 +24,11 @@ class ClassFieldManagerFacade(context: CgContext) : CgContextOwner by context { } fun findTrustedModels(): List { - val trustedModels = mutableListOf() - annotationManagers.forEach { manager -> - val managedModels = manager.annotatedModelGroups[manager.annotationType] - trustedModels += managedModels ?: emptyList() - } + val trustedModels = mutableListOf(UtSpringContextModel.wrap()) - trustedModels += listOf(UtSpringContextModel.wrap()) + relevantFieldManagers.forEach { manager -> + trustedModels += manager.getManagedModels() + } return trustedModels } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/FieldManagerUtils.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/ModelGroupsProvider.kt similarity index 98% rename from utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/FieldManagerUtils.kt rename to utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/ModelGroupsProvider.kt index 9502468a2b..7b42558f12 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/FieldManagerUtils.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/fieldmanager/ModelGroupsProvider.kt @@ -24,7 +24,7 @@ data class ModelsByOrigin( val stateBeforeDependentModels: Set, ) -class FieldManagerUtils(val context: CgContext): CgContextOwner by context { +class ModelGroupsProvider(val context: CgContext): CgContextOwner by context { private val modelsCache = mutableMapOf()