diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/assemble/AssembleModelGenerator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/assemble/AssembleModelGenerator.kt index 9f98126be8..19965f2fb9 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/assemble/AssembleModelGenerator.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/assemble/AssembleModelGenerator.kt @@ -358,23 +358,23 @@ class AssembleModelGenerator(private val basePackageName: String) { private fun assembleMockCompositeModel(compositeModel: UtCompositeModel): UtCompositeModel { // We have to create a model before the construction of the fields to avoid // infinite recursion when some mock contains itself as a field. - val assembledModel = UtCompositeModel( + val assembledCompositeModel = UtCompositeModel( compositeModel.id, compositeModel.classId, isMock = true, ) - instantiatedModels[compositeModel] = assembledModel + instantiatedModels[compositeModel] = assembledCompositeModel val fields = compositeModel.fields.mapValues { assembleModel(it.value) }.toMutableMap() val mockBehaviour = compositeModel.mocks .mapValues { models -> models.value.map { assembleModel(it) } } .toMutableMap() - assembledModel.fields += fields - assembledModel.mocks += mockBehaviour + assembledCompositeModel.fields += fields + assembledCompositeModel.mocks += mockBehaviour - return assembledModel + return assembledCompositeModel } /** diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/CodeGenerator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/CodeGenerator.kt index ebb662c064..f921cec3b0 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/CodeGenerator.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/CodeGenerator.kt @@ -8,13 +8,16 @@ import org.utbot.framework.codegen.domain.RuntimeExceptionTestsBehaviour import org.utbot.framework.codegen.domain.StaticsMocking import org.utbot.framework.codegen.domain.TestFramework import org.utbot.framework.codegen.domain.models.CgMethodTestSet -import org.utbot.framework.codegen.domain.models.TestClassModel import org.utbot.framework.codegen.domain.context.CgContext +import org.utbot.framework.codegen.domain.models.CgClassFile +import org.utbot.framework.codegen.domain.models.builders.SimpleTestClassModelBuilder +import org.utbot.framework.codegen.domain.models.builders.SpringTestClassModelBuilder import org.utbot.framework.codegen.renderer.CgAbstractRenderer import org.utbot.framework.codegen.reports.TestsGenerationReport import org.utbot.framework.codegen.tree.CgSimpleTestClassConstructor import org.utbot.framework.codegen.tree.ututils.UtilClassKind import org.utbot.framework.codegen.services.language.CgLanguageAssistant +import org.utbot.framework.codegen.tree.CgSpringTestClassConstructor import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.CodegenLanguage import org.utbot.framework.plugin.api.ExecutableId @@ -73,29 +76,60 @@ open class CodeGenerator( val cgTestSets = testSets.map { CgMethodTestSet(it) }.toList() return withCustomContext(testClassCustomName) { context.withTestClassFileScope { - val astConstructor = CgSimpleTestClassConstructor(context) - val renderer = CgAbstractRenderer.makeRenderer(context) - val testClassModel = TestClassModel.fromTestSets(classUnderTest, cgTestSets) + if (context.isSpringClass) { + generateForSpringClass(cgTestSets) + } else { + generateForSimpleClass(cgTestSets) + } + } + } + } - fun now() = LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss.SSS")) + private fun generateForSimpleClass(testSets: List): CodeGeneratorResult { + val astConstructor = CgSimpleTestClassConstructor(context) + val testClassModel = SimpleTestClassModelBuilder().createTestClassModel(classUnderTest, testSets) - logger.info { "Code generation phase started at ${now()}" } - val testClassFile = astConstructor.construct(testClassModel) - logger.info { "Code generation phase finished at ${now()}" } + logger.info { "Code generation phase started at ${now()}" } + val testClassFile = astConstructor.construct(testClassModel) + logger.info { "Code generation phase finished at ${now()}" } - logger.info { "Rendering phase started at ${now()}" } - testClassFile.accept(renderer) - logger.info { "Rendering phase finished at ${now()}" } + val generatedCode = renderToString(testClassFile) - CodeGeneratorResult( - generatedCode = renderer.toString(), - utilClassKind = UtilClassKind.fromCgContextOrNull(context), - testsGenerationReport = astConstructor.testsGenerationReport - ) - } - } + return CodeGeneratorResult( + generatedCode = generatedCode, + utilClassKind = UtilClassKind.fromCgContextOrNull(context), + testsGenerationReport = astConstructor.testsGenerationReport + ) + } + + private fun generateForSpringClass(testSets: List): CodeGeneratorResult { + val astConstructor = CgSpringTestClassConstructor(context) + val testClassModel = SpringTestClassModelBuilder().createTestClassModel(classUnderTest, testSets) + + logger.info { "Code generation phase started at ${now()}" } + val testClassFile = astConstructor.construct(testClassModel) + logger.info { "Code generation phase finished at ${now()}" } + + val generatedCode = renderToString(testClassFile) + + return CodeGeneratorResult( + generatedCode = generatedCode, + utilClassKind = UtilClassKind.fromCgContextOrNull(context), + testsGenerationReport = TestsGenerationReport() + ) } + private fun renderToString(testClassFile: CgClassFile): String { + logger.info { "Rendering phase started at ${now()}" } + val renderer = CgAbstractRenderer.makeRenderer(context) + testClassFile.accept(renderer) + logger.info { "Rendering phase finished at ${now()}" } + + return renderer.toString() + } + + private fun now() = LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss.SSS")) + /** * Wrapper function that configures context as needed for utbot-online: * - turns on imports optimization in code generator 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 356d07270a..7a7ebc5309 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 @@ -26,7 +26,7 @@ import org.utbot.framework.codegen.domain.models.CgMethodTestSet import org.utbot.framework.codegen.domain.builtin.TestClassUtilMethodProvider import org.utbot.framework.codegen.domain.builtin.UtilClassFileMethodProvider import org.utbot.framework.codegen.domain.builtin.UtilMethodProvider -import org.utbot.framework.codegen.domain.models.TestClassModel +import org.utbot.framework.codegen.domain.models.SimpleTestClassModel import org.utbot.framework.codegen.domain.models.CgParameterKind import org.utbot.framework.codegen.services.access.Block import org.utbot.framework.codegen.tree.EnvironmentFieldStateCache @@ -66,6 +66,9 @@ interface CgContextOwner { // current class under test val classUnderTest: ClassId + // If test class is configured with Spring, we should do some extra analysis + val isSpringClass: Boolean + // test class currently being generated (if series of nested classes is generated, it is the outermost one) val outerMostTestClass: ClassId @@ -292,7 +295,7 @@ interface CgContextOwner { * This method does almost all the same as [withTestClassScope], but for nested test classes. * The difference is that instead of working with [outerMostTestClassContext] it works with [currentTestClassContext]. */ - fun withNestedClassScope(testClassModel: TestClassModel, block: () -> R): R + fun withNestedClassScope(testClassModel: SimpleTestClassModel, block: () -> R): R /** * Set [mockFrameworkUsed] flag to true if the block is successfully executed @@ -431,6 +434,7 @@ interface CgContextOwner { */ data class CgContext( override val classUnderTest: ClassId, + override val isSpringClass: Boolean = false, val generateUtilClassFile: Boolean = false, override var currentExecutable: ExecutableId? = null, override val collectedExceptions: MutableSet = mutableSetOf(), @@ -538,7 +542,7 @@ data class CgContext( } } - override fun withNestedClassScope(testClassModel: TestClassModel, block: () -> R): R { + override fun withNestedClassScope(testClassModel: SimpleTestClassModel, block: () -> R): R { val previousCurrentTestClassInfo = currentTestClassContext val previousCurrentTestClass = currentTestClass currentTestClass = createClassIdForNestedClass(testClassModel) @@ -551,7 +555,7 @@ data class CgContext( } } - private fun createClassIdForNestedClass(testClassModel: TestClassModel): ClassId { + private fun createClassIdForNestedClass(testClassModel: SimpleTestClassModel): ClassId { val simpleName = "${testClassModel.classUnderTest.simpleName}Test" return BuiltinClassId( canonicalName = currentTestClass.canonicalName + "." + simpleName, 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 1786212e9d..c3adea18e8 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 @@ -1,58 +1,35 @@ package org.utbot.framework.codegen.domain.models import org.utbot.framework.plugin.api.ClassId -import org.utbot.framework.plugin.api.util.enclosingClass -// TODO: seems like this class needs to be renamed /** - * Stores method testsets in a structure that replicates structure of their methods in [classUnderTest]. + * Stores method test sets in a structure that replicates structure of their methods in [classUnderTest]. * I.e., if some method is declared in nested class of [classUnderTest], its testset will be put * in [TestClassModel] in one of [nestedClasses] */ -data class TestClassModel( +abstract class TestClassModel( val classUnderTest: ClassId, val methodTestSets: List, - val nestedClasses: List = listOf() -) { - companion object { - fun fromTestSets(classUnderTest: ClassId, testSets: List): TestClassModel { - // For each class stores list of methods declared in this class (methods from nested classes are excluded) - val class2methodTestSets = testSets.groupBy { it.executableId.classId } + val nestedClasses: List, +) - val classesWithMethodsUnderTest = testSets - .map { it.executableId.classId } - .distinct() +open class SimpleTestClassModel( + classUnderTest: ClassId, + methodTestSets: List, + nestedClasses: List = listOf(), +): TestClassModel(classUnderTest, methodTestSets, nestedClasses) - // For each class stores list of its "direct" nested classes - val class2nestedClasses = mutableMapOf>() - - for (classId in classesWithMethodsUnderTest) { - var currentClass = classId - var enclosingClass = currentClass.enclosingClass - // while we haven't reached the top of nested class hierarchy or the main class under test - while (enclosingClass != null && currentClass != classUnderTest) { - class2nestedClasses.getOrPut(enclosingClass) { mutableSetOf() } += currentClass - currentClass = enclosingClass - enclosingClass = enclosingClass.enclosingClass - } - } - return constructRecursively(classUnderTest, class2methodTestSets, class2nestedClasses) - } +/** + * Extended [SimpleTestClassModel] for Spring analysis reasons + * + * @param injectingMocksClass a class to inject other mocks into + * @param mockedClasses variables of test class to represent mocked instances + */ +class SpringTestClassModel( + classUnderTest: ClassId, + methodTestSets: List, + nestedClasses: List, + val injectingMocksClass: ClassId? = null, + val mockedClasses: Set = setOf(), +): TestClassModel(classUnderTest, methodTestSets, nestedClasses) - private fun constructRecursively( - clazz: ClassId, - class2methodTestSets: Map>, - class2nestedClasses: Map> - ): TestClassModel { - val currentNestedClasses = class2nestedClasses.getOrDefault(clazz, listOf()) - val currentMethodTestSets = class2methodTestSets.getOrDefault(clazz, listOf()) - return TestClassModel( - clazz, - currentMethodTestSets, - currentNestedClasses.map { - constructRecursively(it, class2methodTestSets, class2nestedClasses) - } - ) - } - } -} \ No newline at end of file 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 new file mode 100644 index 0000000000..cefcc27249 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/builders/SimpleTestClassModelBuilder.kt @@ -0,0 +1,49 @@ +package org.utbot.framework.codegen.domain.models.builders + +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: TestClassModelBuilder() { + override fun createTestClassModel(classUnderTest: ClassId, testSets: List): SimpleTestClassModel { + // For each class stores list of methods declared in this class (methods from nested classes are excluded) + val class2methodTestSets = testSets.groupBy { it.executableId.classId } + + val classesWithMethodsUnderTest = testSets + .map { it.executableId.classId } + .distinct() + + // For each class stores list of its "direct" nested classes + val class2nestedClasses = mutableMapOf>() + + for (classId in classesWithMethodsUnderTest) { + var currentClass = classId + var enclosingClass = currentClass.enclosingClass + // while we haven't reached the top of nested class hierarchy or the main class under test + while (enclosingClass != null && currentClass != classUnderTest) { + class2nestedClasses.getOrPut(enclosingClass) { mutableSetOf() } += currentClass + currentClass = enclosingClass + enclosingClass = enclosingClass.enclosingClass + } + } + + return constructRecursively(classUnderTest, class2methodTestSets, class2nestedClasses) + } + + private fun constructRecursively( + clazz: ClassId, + class2methodTestSets: Map>, + class2nestedClasses: Map> + ): SimpleTestClassModel { + val currentNestedClasses = class2nestedClasses.getOrDefault(clazz, listOf()) + val currentMethodTestSets = class2methodTestSets.getOrDefault(clazz, listOf()) + return SimpleTestClassModel( + clazz, + currentMethodTestSets, + currentNestedClasses.map { + constructRecursively(it, class2methodTestSets, class2nestedClasses) + } + ) + } +} \ 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/domain/models/builders/SpringTestClassModelBuilder.kt new file mode 100644 index 0000000000..fe7c0e820e --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/builders/SpringTestClassModelBuilder.kt @@ -0,0 +1,112 @@ +package org.utbot.framework.codegen.domain.models.builders + +import org.utbot.framework.codegen.domain.models.CgMethodTestSet +import org.utbot.framework.codegen.domain.models.SpringTestClassModel +import org.utbot.framework.plugin.api.ClassId +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.UtDirectSetFieldModel +import org.utbot.framework.plugin.api.UtEnumConstantModel +import org.utbot.framework.plugin.api.UtExecutableCallModel +import org.utbot.framework.plugin.api.UtExecutionSuccess +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.UtVoidModel + +class SpringTestClassModelBuilder: TestClassModelBuilder() { + + override fun createTestClassModel(classUnderTest: ClassId, testSets: List): SpringTestClassModel { + val baseModel = SimpleTestClassModelBuilder().createTestClassModel(classUnderTest, testSets) + val mockedClasses = collectMockedClassIds(classUnderTest, testSets) + + return SpringTestClassModel( + baseModel.classUnderTest, + baseModel.methodTestSets, + baseModel.nestedClasses, + classUnderTest, + mockedClasses, + ) + } + + private fun collectMockedClassIds( + classUnderTest: ClassId, + testSets: List, + ): Set { + val allModelsInExecution = mutableListOf() + + for (testSet in testSets) { + for (execution in testSet.executions) { + execution.stateBefore.thisInstance?.let { allModelsInExecution += it } + execution.stateAfter.thisInstance?.let { allModelsInExecution += it } + + allModelsInExecution += execution.stateBefore.parameters + allModelsInExecution += execution.stateAfter.parameters + + (execution.result as? UtExecutionSuccess)?.model?.let { allModelsInExecution += it } + } + } + + val allConstructedModels = mutableSetOf() + allModelsInExecution.forEach { model -> collectRecursively(model, allConstructedModels) } + + return allConstructedModels + .filter { it.isMockComposite() || it.isMockAssemble() } + .map { it.classId } + .filter { it != classUnderTest } + .toSet() + + } + + private fun collectRecursively(currentModel: UtModel, allModels: MutableSet) { + if (currentModel in allModels) { + return + } + + allModels += currentModel + + when (currentModel) { + is UtNullModel, + is UtPrimitiveModel, + is UtClassRefModel, + is UtVoidModel, + is UtEnumConstantModel -> {} + is UtLambdaModel -> { + currentModel.capturedValues.forEach { collectRecursively(it, allModels) } + } + is UtArrayModel -> { + currentModel.stores.values.forEach { collectRecursively(it, allModels) } + if (currentModel.stores.count() < currentModel.length) { + collectRecursively(currentModel.constModel, allModels) + } + } + is UtCompositeModel -> { + currentModel.fields.values.forEach { collectRecursively(it, allModels) } + currentModel.mocks.values.asSequence().flatten().forEach { collectRecursively(it, allModels) } + } + is UtAssembleModel -> { + currentModel.origin?.let { collectRecursively(it, allModels) } + + currentModel.instantiationCall.instance?.let { collectRecursively(it, allModels) } + currentModel.instantiationCall.params.forEach { collectRecursively(it, allModels) } + + currentModel.modificationsChain.forEach { stmt -> + stmt.instance?.let { collectRecursively(it, allModels) } + when (stmt) { + is UtExecutableCallModel -> stmt.params.forEach { collectRecursively(it, allModels) } + is UtDirectSetFieldModel -> collectRecursively(stmt.fieldModel, allModels) + } + } + } + //Python, JavaScript, Go models are not required in Spring + } + } + + private fun UtModel.isMockComposite(): Boolean = this is UtCompositeModel && this.isMock + + //TODO: Having an assemble model often means that we do not use its origin, so is this composite mock redundant? + private fun UtModel.isMockAssemble(): Boolean = this is UtAssembleModel && this.origin?.isMock == true +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/builders/TestClassModelBuilder.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/builders/TestClassModelBuilder.kt new file mode 100644 index 0000000000..a1f47e873e --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/builders/TestClassModelBuilder.kt @@ -0,0 +1,9 @@ +package org.utbot.framework.codegen.domain.models.builders + +import org.utbot.framework.codegen.domain.models.CgMethodTestSet +import org.utbot.framework.codegen.domain.models.TestClassModel +import org.utbot.framework.plugin.api.ClassId + +abstract class TestClassModelBuilder { + abstract fun createTestClassModel(classUnderTest: ClassId, testSets: List): TestClassModel +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSimpleTestClassConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSimpleTestClassConstructor.kt index 78711d867b..879a7a67dd 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSimpleTestClassConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSimpleTestClassConstructor.kt @@ -8,6 +8,7 @@ import org.utbot.framework.codegen.domain.TestNg import org.utbot.framework.codegen.domain.builtin.TestClassUtilMethodProvider import org.utbot.framework.codegen.domain.context.CgContext import org.utbot.framework.codegen.domain.models.CgAuxiliaryClass +import org.utbot.framework.codegen.domain.models.CgClassBody import org.utbot.framework.codegen.domain.models.CgMethod import org.utbot.framework.codegen.domain.models.CgMethodTestSet import org.utbot.framework.codegen.domain.models.CgMethodsCluster @@ -20,6 +21,7 @@ import org.utbot.framework.codegen.domain.models.CgTestMethodCluster import org.utbot.framework.codegen.domain.models.CgTripleSlashMultilineComment import org.utbot.framework.codegen.domain.models.CgUtilEntity import org.utbot.framework.codegen.domain.models.CgUtilMethod +import org.utbot.framework.codegen.domain.models.SimpleTestClassModel import org.utbot.framework.codegen.domain.models.TestClassModel import org.utbot.framework.codegen.renderer.importUtilMethodDependencies import org.utbot.framework.codegen.reports.TestsGenerationReport @@ -37,7 +39,7 @@ import org.utbot.fuzzer.UtFuzzedExecution /** * This test class constructor is used for pure Java/Kotlin applications. */ -open class CgSimpleTestClassConstructor(context: CgContext): CgTestClassConstructorBase(context) { +open class CgSimpleTestClassConstructor(context: CgContext): CgTestClassConstructorBase(context) { init { clearContextRelatedStorage() @@ -47,56 +49,57 @@ open class CgSimpleTestClassConstructor(context: CgContext): CgTestClassConstruc val testsGenerationReport = TestsGenerationReport() - override fun constructTestClassBody(testClassModel: TestClassModel) = buildClassBody(currentTestClass) { - val notYetConstructedTestSets = testClassModel.methodTestSets.toMutableList() - - for (nestedClass in testClassModel.nestedClasses) { - // It is not possible to run tests for both outer and inner class in JUnit4 at once, - // so we locate all test methods in outer test class for JUnit4. - // see https://stackoverflow.com/questions/69770700/how-to-run-tests-from-outer-class-and-nested-inner-classes-simultaneously-in-jun - // or https://stackoverflow.com/questions/28230277/test-cases-in-inner-class-and-outer-class-with-junit4 - when (testFramework) { - Junit4 -> { - notYetConstructedTestSets += collectTestSetsFromInnerClasses(nestedClass) - } - Junit5, - TestNg -> { - nestedClassRegions += CgRealNestedClassesRegion( - "Tests for ${nestedClass.classUnderTest.simpleName}", - listOf(withNestedClassScope(nestedClass) { constructTestClass(nestedClass) }) - ) + override fun constructTestClassBody(testClassModel: SimpleTestClassModel): CgClassBody = + buildClassBody(currentTestClass) { + val notYetConstructedTestSets = testClassModel.methodTestSets.toMutableList() + + for (nestedClass in testClassModel.nestedClasses) { + // It is not possible to run tests for both outer and inner class in JUnit4 at once, + // so we locate all test methods in outer test class for JUnit4. + // see https://stackoverflow.com/questions/69770700/how-to-run-tests-from-outer-class-and-nested-inner-classes-simultaneously-in-jun + // or https://stackoverflow.com/questions/28230277/test-cases-in-inner-class-and-outer-class-with-junit4 + when (testFramework) { + Junit4 -> { + notYetConstructedTestSets += collectTestSetsFromInnerClasses(nestedClass) + } + Junit5, + TestNg -> { + nestedClassRegions += CgRealNestedClassesRegion( + "Tests for ${nestedClass.classUnderTest.simpleName}", + listOf(withNestedClassScope(nestedClass) { constructTestClass(nestedClass) }) + ) + } } } - } - - for (testSet in notYetConstructedTestSets) { - updateCurrentExecutable(testSet.executableId) - val currentMethodUnderTestRegions = constructTestSet(testSet) ?: continue - val executableUnderTestCluster = CgMethodsCluster( - "Test suites for executable $currentExecutable", - currentMethodUnderTestRegions - ) - methodRegions += executableUnderTestCluster - } - val currentTestClassDataProviderMethods = currentTestClassContext.cgDataProviderMethods - if (currentTestClassDataProviderMethods.isNotEmpty()) { - staticDeclarationRegions += - CgStaticsRegion( - "Data provider methods for parametrized tests", - currentTestClassDataProviderMethods, + for (testSet in notYetConstructedTestSets) { + updateCurrentExecutable(testSet.executableId) + val currentMethodUnderTestRegions = constructTestSet(testSet) ?: continue + val executableUnderTestCluster = CgMethodsCluster( + "Test suites for executable $currentExecutable", + currentMethodUnderTestRegions ) - } + methodRegions += executableUnderTestCluster + } - if (currentTestClass == outerMostTestClass) { - val utilEntities = collectUtilEntities() - // If utilMethodProvider is TestClassUtilMethodProvider, then util entities should be declared - // in the test class. Otherwise, util entities will be located elsewhere (e.g. another class). - if (utilMethodProvider is TestClassUtilMethodProvider && utilEntities.isNotEmpty()) { - staticDeclarationRegions += CgStaticsRegion("Util methods", utilEntities) + val currentTestClassDataProviderMethods = currentTestClassContext.cgDataProviderMethods + if (currentTestClassDataProviderMethods.isNotEmpty()) { + staticDeclarationRegions += + CgStaticsRegion( + "Data provider methods for parametrized tests", + currentTestClassDataProviderMethods, + ) + } + + if (currentTestClass == outerMostTestClass) { + val utilEntities = collectUtilEntities() + // If utilMethodProvider is TestClassUtilMethodProvider, then util entities should be declared + // in the test class. Otherwise, util entities will be located elsewhere (e.g. another class). + if (utilMethodProvider is TestClassUtilMethodProvider && utilEntities.isNotEmpty()) { + staticDeclarationRegions += CgStaticsRegion("Util methods", utilEntities) + } } } - } private fun constructTestSet(testSet: CgMethodTestSet): List>? { val regions = mutableListOf>() @@ -128,7 +131,7 @@ open class CgSimpleTestClassConstructor(context: CgContext): CgTestClassConstruc return if (regions.any()) regions else null } - private fun collectTestSetsFromInnerClasses(model: TestClassModel): List { + private fun collectTestSetsFromInnerClasses(model: SimpleTestClassModel): List { val testSets = model.methodTestSets.toMutableList() for (nestedClass in model.nestedClasses) { testSets += collectTestSetsFromInnerClasses(nestedClass) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSpringTestClassConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSpringTestClassConstructor.kt new file mode 100644 index 0000000000..b11b48bae8 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSpringTestClassConstructor.kt @@ -0,0 +1,15 @@ +package org.utbot.framework.codegen.tree + +import org.utbot.framework.codegen.domain.context.CgContext +import org.utbot.framework.codegen.domain.models.CgClassBody +import org.utbot.framework.codegen.domain.models.SpringTestClassModel + +class CgSpringTestClassConstructor(context: CgContext): CgTestClassConstructorBase(context) { + + override val methodConstructor: CgMethodConstructor + get() = TODO("Not yet implemented") + + override fun constructTestClassBody(testClassModel: SpringTestClassModel): CgClassBody { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgTestClassConstructorBase.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgTestClassConstructorBase.kt index dcf5d24c7d..eecac53277 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgTestClassConstructorBase.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgTestClassConstructorBase.kt @@ -5,11 +5,12 @@ import org.utbot.framework.codegen.domain.context.CgContextOwner import org.utbot.framework.codegen.domain.models.CgClass import org.utbot.framework.codegen.domain.models.CgClassBody import org.utbot.framework.codegen.domain.models.CgClassFile +import org.utbot.framework.codegen.domain.models.SimpleTestClassModel import org.utbot.framework.codegen.domain.models.TestClassModel import org.utbot.framework.codegen.services.CgNameGenerator import org.utbot.framework.codegen.services.framework.TestFrameworkManager -abstract class CgTestClassConstructorBase(val context: CgContext): +abstract class CgTestClassConstructorBase(val context: CgContext): CgContextOwner by context, CgStatementConstructor by CgComponents.getStatementConstructorBy(context){ @@ -20,7 +21,7 @@ abstract class CgTestClassConstructorBase(val context: CgContext): /** * Constructs a file with the test class corresponding to [TestClassModel]. */ - open fun construct(testClassModel: TestClassModel): CgClassFile { + open fun construct(testClassModel: T): CgClassFile { return buildClassFile { this.declaredClass = withTestClassScope { constructTestClass(testClassModel) } imports += context.collectedImports @@ -30,7 +31,7 @@ abstract class CgTestClassConstructorBase(val context: CgContext): /** * Constructs [CgClass] corresponding to [TestClassModel]. */ - open fun constructTestClass(testClassModel: TestClassModel): CgClass { + open fun constructTestClass(testClassModel: T): CgClass { return buildClass { id = currentTestClass @@ -54,5 +55,5 @@ abstract class CgTestClassConstructorBase(val context: CgContext): } } - abstract fun constructTestClassBody(testClassModel: TestClassModel): CgClassBody + abstract fun constructTestClassBody(testClassModel: T): CgClassBody } \ No newline at end of file diff --git a/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/PythonCodeGenerator.kt b/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/PythonCodeGenerator.kt index 60767d1cef..2e911830f6 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/PythonCodeGenerator.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/PythonCodeGenerator.kt @@ -12,7 +12,7 @@ import org.utbot.framework.codegen.domain.models.CgAssignment import org.utbot.framework.codegen.domain.models.CgLiteral import org.utbot.framework.codegen.domain.models.CgMethodTestSet import org.utbot.framework.codegen.domain.models.CgVariable -import org.utbot.framework.codegen.domain.models.TestClassModel +import org.utbot.framework.codegen.domain.models.SimpleTestClassModel import org.utbot.framework.codegen.renderer.CgAbstractRenderer import org.utbot.framework.codegen.renderer.CgPrinterImpl import org.utbot.framework.codegen.renderer.CgRendererContext @@ -79,7 +79,7 @@ class PythonCodeGenerator( ): CodeGeneratorResult = withCustomContext(testClassCustomName) { context.withTestClassFileScope { (context.cgLanguageAssistant as PythonCgLanguageAssistant).clear() - val testClassModel = TestClassModel(classUnderTest, cgTestSets) + val testClassModel = SimpleTestClassModel(classUnderTest, cgTestSets) context.collectedImports.addAll(importModules) val astConstructor = PythonCgTestClassConstructor(context) diff --git a/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/tree/PythonCgTestClassConstructor.kt b/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/tree/PythonCgTestClassConstructor.kt index 593f64613f..665b1c9f16 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/tree/PythonCgTestClassConstructor.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/tree/PythonCgTestClassConstructor.kt @@ -1,13 +1,13 @@ package org.utbot.python.framework.codegen.model.constructor.tree -import org.utbot.framework.codegen.domain.models.TestClassModel +import org.utbot.framework.codegen.domain.models.SimpleTestClassModel import org.utbot.framework.codegen.domain.context.CgContext import org.utbot.framework.codegen.domain.models.CgClassFile import org.utbot.framework.codegen.tree.CgSimpleTestClassConstructor import org.utbot.framework.codegen.tree.buildClassFile internal class PythonCgTestClassConstructor(context: CgContext) : CgSimpleTestClassConstructor(context) { - override fun construct(testClassModel: TestClassModel): CgClassFile { + override fun construct(testClassModel: SimpleTestClassModel): CgClassFile { return buildClassFile { this.declaredClass = withTestClassScope { with(currentTestClassContext) { testClassSuperclass = testFramework.testSuperClass }