diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/CgMethodTestSet.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/CgMethodTestSet.kt index 80994c49b4..e241083e8c 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/CgMethodTestSet.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/CgMethodTestSet.kt @@ -32,11 +32,49 @@ data class CgMethodTestSet private constructor( executions = from.executions } + fun prepareTestSetsForParameterizedTestGeneration(): List { + val testSetList = mutableListOf() + + // Mocks are not supported in parametrized tests, so we exclude them + val testSetWithoutMocking = this.excludeExecutionsWithMocking() + for (splitByExecutionTestSet in testSetWithoutMocking.splitExecutionsByResult()) { + for (splitByChangedStaticsTestSet in splitByExecutionTestSet.splitExecutionsByChangedStatics()) { + testSetList += splitByChangedStaticsTestSet + } + } + + return testSetList + } + + /** + * Finds a [ClassId] of all result models in executions. + * + * Tries to find a unique result type in testSets or + * gets executable return type. + */ + fun resultType(): ClassId { + return when (executableId.returnType) { + voidClassId -> executableId.returnType + else -> { + val successfulExecutions = executions.filter { it.result is UtExecutionSuccess } + if (successfulExecutions.isNotEmpty()) { + successfulExecutions + .map { (it.result as UtExecutionSuccess).model.classId } + .distinct() + .singleOrNull() + ?: executableId.returnType + } else { + executableId.returnType + } + } + } + } + /** * Splits [CgMethodTestSet] into separate test sets having * unique result model [ClassId] in each subset. */ - fun splitExecutionsByResult() : List { + private fun splitExecutionsByResult() : List { val successfulExecutions = executions.filter { it.result is UtExecutionSuccess } val failureExecutions = executions.filter { it.result is UtExecutionFailure } @@ -60,47 +98,26 @@ data class CgMethodTestSet private constructor( * * A separate test set is created for each combination of modified statics. */ - fun splitExecutionsByChangedStatics(): List { + private fun splitExecutionsByChangedStatics(): List { val executionsByStaticsUsage: Map, List> = executions.groupBy { it.stateBefore.statics.keys } return executionsByStaticsUsage.map { (_, executions) -> substituteExecutions(executions) } } - /* - * Excludes executions with mocking from [CgMethodTestSet]. - * */ - fun excludeExecutionsWithMocking(): CgMethodTestSet { - val fuzzedExecutions = executions.filterIsInstance() + /** + * Excludes [UtFuzzedExecution] and [UtSymbolicExecution] with mocking from [CgMethodTestSet]. + * + * It is used in parameterized test generation. + * We exclude them because we cannot track force mocking occurrences in fuzzing process + * and cannot deal with mocking in parameterized mode properly. + */ + private fun excludeExecutionsWithMocking(): CgMethodTestSet { val symbolicExecutionsWithoutMocking = executions .filterIsInstance() .filter { !it.containsMocking } - return substituteExecutions(symbolicExecutionsWithoutMocking + fuzzedExecutions) - } - - /** - * Finds a [ClassId] of all result models in executions. - * - * Tries to find a unique result type in testSets or - * gets executable return type. - */ - fun resultType(): ClassId { - return when (executableId.returnType) { - voidClassId -> executableId.returnType - else -> { - val successfulExecutions = executions.filter { it.result is UtExecutionSuccess } - if (successfulExecutions.isNotEmpty()) { - successfulExecutions - .map { (it.result as UtExecutionSuccess).model.classId } - .distinct() - .singleOrNull() - ?: executableId.returnType - } else { - executableId.returnType - } - } - } + return substituteExecutions(symbolicExecutionsWithoutMocking) } private fun substituteExecutions(newExecutions: List): CgMethodTestSet = diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgTestClassConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgTestClassConstructor.kt index a404b2826f..61a77517dd 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgTestClassConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgTestClassConstructor.kt @@ -21,7 +21,6 @@ import org.utbot.framework.codegen.model.constructor.util.CgStatementConstructor import org.utbot.framework.codegen.model.tree.CgAuxiliaryClass import org.utbot.framework.codegen.model.tree.CgExecutableUnderTestCluster import org.utbot.framework.codegen.model.tree.CgMethod -import org.utbot.framework.codegen.model.tree.CgParameterDeclaration import org.utbot.framework.codegen.model.tree.CgRegion import org.utbot.framework.codegen.model.tree.CgSimpleRegion import org.utbot.framework.codegen.model.tree.CgStaticsRegion @@ -37,12 +36,12 @@ import org.utbot.framework.codegen.model.tree.buildTestClassBody import org.utbot.framework.codegen.model.tree.buildTestClassFile import org.utbot.framework.codegen.model.visitor.importUtilMethodDependencies import org.utbot.framework.plugin.api.ClassId -import org.utbot.framework.plugin.api.ExecutableId import org.utbot.framework.plugin.api.MethodId import org.utbot.framework.plugin.api.UtExecutionSuccess import org.utbot.framework.plugin.api.UtMethodTestSet import org.utbot.framework.plugin.api.util.description import org.utbot.framework.plugin.api.util.humanReadableName +import org.utbot.fuzzer.UtFuzzedExecution internal class CgTestClassConstructor(val context: CgContext) : CgContextOwner by context, @@ -140,45 +139,18 @@ internal class CgTestClassConstructor(val context: CgContext) : .filter { it.result is UtExecutionSuccess } .map { (it.result as UtExecutionSuccess).model } - val (methodUnderTest, _, _, clustersInfo) = testSet val regions = mutableListOf>() - val requiredFields = mutableListOf() - - when (context.parametrizedTestSource) { - ParametrizedTestSource.DO_NOT_PARAMETRIZE -> { - for ((clusterSummary, executionIndices) in clustersInfo) { - val currentTestCaseTestMethods = mutableListOf() - emptyLineIfNeeded() - for (i in executionIndices) { - runCatching { - currentTestCaseTestMethods += methodConstructor.createTestMethod(methodUnderTest, testSet.executions[i]) - }.onFailure { e -> processFailure(testSet, e) } - } - val clusterHeader = clusterSummary?.header - val clusterContent = clusterSummary?.content - ?.split('\n') - ?.let { CgTripleSlashMultilineComment(it) } - regions += CgTestMethodCluster(clusterHeader, clusterContent, currentTestCaseTestMethods) - testsGenerationReport.addTestsByType(testSet, currentTestCaseTestMethods) - } - } - ParametrizedTestSource.PARAMETRIZE -> { - // Mocks are not supported in parametrized tests, we should exclude them - val testSetWithoutMocking = testSet.excludeExecutionsWithMocking() - - for (splitByExecutionTestSet in testSetWithoutMocking.splitExecutionsByResult()) { - for (splitByChangedStaticsTestSet in splitByExecutionTestSet.splitExecutionsByChangedStatics()) { - createParametrizedTestAndDataProvider( - splitByChangedStaticsTestSet, - requiredFields, - regions, - methodUnderTest - ) - } - } + runCatching { + when (context.parametrizedTestSource) { + ParametrizedTestSource.DO_NOT_PARAMETRIZE -> createTest(testSet, regions) + ParametrizedTestSource.PARAMETRIZE -> + createParametrizedTestAndDataProvider( + testSet, + regions + ) } - } + }.onFailure { e -> processFailure(testSet, e) } val errors = testSet.allErrors if (errors.isNotEmpty()) { @@ -195,29 +167,62 @@ internal class CgTestClassConstructor(val context: CgContext) : .merge(failure.description, 1, Int::plus) } + private fun createTest( + testSet: CgMethodTestSet, + regions: MutableList> + ) { + val (methodUnderTest, _, _, clustersInfo) = testSet + + for ((clusterSummary, executionIndices) in clustersInfo) { + val currentTestCaseTestMethods = mutableListOf() + emptyLineIfNeeded() + for (i in executionIndices) { + currentTestCaseTestMethods += methodConstructor.createTestMethod(methodUnderTest, testSet.executions[i]) + } + val clusterHeader = clusterSummary?.header + val clusterContent = clusterSummary?.content + ?.split('\n') + ?.let { CgTripleSlashMultilineComment(it) } + regions += CgTestMethodCluster(clusterHeader, clusterContent, currentTestCaseTestMethods) + + testsGenerationReport.addTestsByType(testSet, currentTestCaseTestMethods) + } + } + private fun createParametrizedTestAndDataProvider( testSet: CgMethodTestSet, - requiredFields: MutableList, - regions: MutableList>, - methodUnderTest: ExecutableId, + regions: MutableList> ) { - runCatching { - val dataProviderMethodName = nameGenerator.dataProviderMethodNameFor(testSet.executableId) + val (methodUnderTest, _, _, _) = testSet - val parameterizedTestMethod = - methodConstructor.createParameterizedTestMethod(testSet, dataProviderMethodName) + for (preparedTestSet in testSet.prepareTestSetsForParameterizedTestGeneration()) { + val dataProviderMethodName = nameGenerator.dataProviderMethodNameFor(preparedTestSet.executableId) - requiredFields += parameterizedTestMethod.requiredFields + val parameterizedTestMethod = + methodConstructor.createParameterizedTestMethod(preparedTestSet, dataProviderMethodName) testFrameworkManager.addDataProvider( - methodConstructor.createParameterizedTestDataProvider(testSet, dataProviderMethodName) + methodConstructor.createParameterizedTestDataProvider(preparedTestSet, dataProviderMethodName) ) regions += CgSimpleRegion( "Parameterized test for method ${methodUnderTest.humanReadableName}", listOf(parameterizedTestMethod), ) - }.onFailure { error -> processFailure(testSet, error) } + } + + // We cannot track mocking in fuzzed executions, + // so we generate standard tests for each [UtFuzzedExecution]. + // [https://github.com/UnitTestBot/UTBotJava/issues/1137] + val testCaseTestMethods = mutableListOf() + for (execution in testSet.executions.filterIsInstance()) { + testCaseTestMethods += methodConstructor.createTestMethod(methodUnderTest, execution) + } + + regions += CgSimpleRegion( + "FUZZER: EXECUTIONS for method ${methodUnderTest.humanReadableName}", + testCaseTestMethods, + ) } /**