From 457c9f6764758f424c02c803d2f9b9c37df85d68 Mon Sep 17 00:00:00 2001 From: Andrey Tarbeev Date: Wed, 27 Jul 2022 21:04:26 +0300 Subject: [PATCH] Add parametrized test generation for methods without force mocking --- .../constructor/tree/CgMethodConstructor.kt | 2 +- .../org/utbot/framework/util/TestUtils.kt | 12 +++++- .../examples/CodeGenerationIntegrationTest.kt | 5 ++- .../examples/TestSpecificTestCaseGenerator.kt | 34 ++++++++++------ .../utbot/examples/UtModelTestCaseChecker.kt | 9 +++++ .../utbot/examples/UtValueTestCaseChecker.kt | 19 +++++++++ .../codegen/TestCodeGeneratorPipeline.kt | 39 +++++++------------ 7 files changed, 78 insertions(+), 42 deletions(-) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt index 3b559d8c72..5d02dc403a 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt @@ -1277,7 +1277,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c // this instance val thisInstanceModel = genericExecution.stateBefore.thisInstance if (thisInstanceModel != null) { - val type = thisInstanceModel.classId + val type = wrapTypeIfRequired(thisInstanceModel.classId) val thisInstance = CgParameterDeclaration( parameter = declareParameter( type = type, diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/util/TestUtils.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/util/TestUtils.kt index ca96f95c0f..e4e0436ebd 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/util/TestUtils.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/util/TestUtils.kt @@ -149,8 +149,16 @@ enum class Conflict { } class ConflictTriggers( - private val triggers: MutableMap = EnumMap(Conflict::class.java).withDefault { false } -): MutableMap by triggers { + private val triggers: MutableMap = EnumMap(Conflict::class.java).also { map -> + Conflict.values().forEach { conflict -> map[conflict] = false } + } +) : MutableMap by triggers { val triggered: Boolean get() = triggers.values.any { it } + + fun reset(vararg conflicts: Conflict) { + for (conflict in conflicts) { + triggers[conflict] = false + } + } } \ No newline at end of file diff --git a/utbot-framework/src/test/kotlin/org/utbot/examples/CodeGenerationIntegrationTest.kt b/utbot-framework/src/test/kotlin/org/utbot/examples/CodeGenerationIntegrationTest.kt index 597e4ff9b7..57aa7dd227 100644 --- a/utbot-framework/src/test/kotlin/org/utbot/examples/CodeGenerationIntegrationTest.kt +++ b/utbot-framework/src/test/kotlin/org/utbot/examples/CodeGenerationIntegrationTest.kt @@ -4,7 +4,6 @@ import org.utbot.common.FileUtil import org.utbot.common.packageName import org.utbot.common.withAccessibility import org.utbot.framework.UtSettings -import org.utbot.framework.codegen.TestCodeGeneratorPipeline.Companion.defaultCodegenPipeline import org.utbot.framework.codegen.ClassStages import org.utbot.framework.codegen.CodeGeneration import org.utbot.framework.codegen.ExecutionStatus @@ -29,6 +28,7 @@ import org.junit.jupiter.api.extension.ExtensionContext import org.junit.jupiter.api.fail import org.junit.jupiter.engine.descriptor.ClassTestDescriptor import org.junit.jupiter.engine.descriptor.JupiterEngineDescriptor +import org.utbot.framework.codegen.TestCodeGeneratorPipeline import java.nio.file.Path @TestInstance(TestInstance.Lifecycle.PER_CLASS) @@ -119,7 +119,8 @@ abstract class CodeGenerationIntegrationTest( ) } - language.defaultCodegenPipeline.runClassesCodeGenerationTests(classStages) + val config = TestCodeGeneratorPipeline.defaultTestFrameworkConfiguration(language) + TestCodeGeneratorPipeline(config).runClassesCodeGenerationTests(classStages) } catch (e: RuntimeException) { pipelineErrors.add(e.message) } diff --git a/utbot-framework/src/test/kotlin/org/utbot/examples/TestSpecificTestCaseGenerator.kt b/utbot-framework/src/test/kotlin/org/utbot/examples/TestSpecificTestCaseGenerator.kt index c4a8caa9b0..55b760e0b4 100644 --- a/utbot-framework/src/test/kotlin/org/utbot/examples/TestSpecificTestCaseGenerator.kt +++ b/utbot-framework/src/test/kotlin/org/utbot/examples/TestSpecificTestCaseGenerator.kt @@ -1,6 +1,7 @@ package org.utbot.examples import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.launch import mu.KotlinLogging import org.utbot.common.runBlockingWithCancellationPredicate import org.utbot.common.runIgnoringCancellationException @@ -10,6 +11,8 @@ import org.utbot.engine.UtBotSymbolicEngine import org.utbot.engine.util.mockListeners.ForceMockListener import org.utbot.engine.util.mockListeners.ForceStaticMockListener import org.utbot.framework.UtSettings +import org.utbot.framework.codegen.ParametrizedTestSource +import org.utbot.framework.codegen.TestCodeGeneratorPipeline import org.utbot.framework.plugin.api.MockStrategyApi import org.utbot.framework.plugin.api.TestCaseGenerator import org.utbot.framework.plugin.api.UtError @@ -47,24 +50,33 @@ class TestSpecificTestCaseGenerator( val mockAlwaysDefaults = Mocker.javaDefaultClasses.mapTo(mutableSetOf()) { it.id } val defaultTimeEstimator = ExecutionTimeEstimator(UtSettings.utBotGenerationTimeoutInMillis, 1) - val forceMockListener = ForceMockListener.create(this, conflictTriggers) - val forceStaticMockListener = ForceStaticMockListener.create(this, conflictTriggers) + val config = TestCodeGeneratorPipeline.currentTestFrameworkConfiguration + var forceMockListener: ForceMockListener? = null + var forceStaticMockListener: ForceStaticMockListener? = null + + if (config.parametrizedTestSource == ParametrizedTestSource.PARAMETRIZE) { + forceMockListener = ForceMockListener.create(this, conflictTriggers) + forceStaticMockListener = ForceStaticMockListener.create(this, conflictTriggers) + } runIgnoringCancellationException { runBlockingWithCancellationPredicate(isCanceled) { - super - .generateAsync(EngineController(), method, mockStrategy, mockAlwaysDefaults, defaultTimeEstimator) - .collect { - when (it) { - is UtExecution -> executions += it - is UtError -> errors.merge(it.description, 1, Int::plus) + val controller = EngineController() + controller.job = launch { + super + .generateAsync(controller, method, mockStrategy, mockAlwaysDefaults, defaultTimeEstimator) + .collect { + when (it) { + is UtExecution -> executions += it + is UtError -> errors.merge(it.description, 1, Int::plus) + } } - } + } } } - forceMockListener.detach(this, forceMockListener) - forceStaticMockListener.detach(this, forceStaticMockListener) + forceMockListener?.detach(this, forceMockListener) + forceStaticMockListener?.detach(this, forceStaticMockListener) val minimizedExecutions = super.minimizeExecutions(executions) return UtMethodTestSet(method, minimizedExecutions, jimpleBody(method), errors) diff --git a/utbot-framework/src/test/kotlin/org/utbot/examples/UtModelTestCaseChecker.kt b/utbot-framework/src/test/kotlin/org/utbot/examples/UtModelTestCaseChecker.kt index 101ff6735f..177f63565b 100644 --- a/utbot-framework/src/test/kotlin/org/utbot/examples/UtModelTestCaseChecker.kt +++ b/utbot-framework/src/test/kotlin/org/utbot/examples/UtModelTestCaseChecker.kt @@ -38,6 +38,8 @@ import kotlin.reflect.KFunction2 import kotlin.reflect.KFunction3 import org.junit.jupiter.api.Assertions.assertTrue import org.utbot.framework.UtSettings.useFuzzing +import org.utbot.framework.codegen.TestCodeGeneratorPipeline +import org.utbot.framework.util.Conflict internal abstract class UtModelTestCaseChecker( testClass: KClass<*>, @@ -99,6 +101,13 @@ internal abstract class UtModelTestCaseChecker( "We have errors: ${testSet.errors.entries.map { "${it.value}: ${it.key}" }.prettify()}" } + // if force mocking took place in parametrized test generation, + // we do not need to process this [testSet] + if (TestCodeGeneratorPipeline.currentTestFrameworkConfiguration.isParametrizedAndMocked) { + conflictTriggers.reset(Conflict.ForceMockHappened, Conflict.ForceStaticMockHappened) + return + } + val executions = testSet.executions assertTrue(branches(executions.size)) { "Branch count matcher '$branches' fails for #executions=${executions.size}: ${executions.prettify()}" diff --git a/utbot-framework/src/test/kotlin/org/utbot/examples/UtValueTestCaseChecker.kt b/utbot-framework/src/test/kotlin/org/utbot/examples/UtValueTestCaseChecker.kt index bbb28788aa..9dbd65868a 100644 --- a/utbot-framework/src/test/kotlin/org/utbot/examples/UtValueTestCaseChecker.kt +++ b/utbot-framework/src/test/kotlin/org/utbot/examples/UtValueTestCaseChecker.kt @@ -54,6 +54,7 @@ import org.utbot.framework.plugin.api.UtValueExecution import org.utbot.framework.plugin.api.util.UtContext import org.utbot.framework.plugin.api.util.executableId import org.utbot.framework.plugin.api.util.withUtContext +import org.utbot.framework.util.Conflict import org.utbot.framework.util.toValueTestCase import org.utbot.summary.summarize import java.io.File @@ -2296,6 +2297,13 @@ abstract class UtValueTestCaseChecker( walk(utMethod, mockStrategy) } + // if force mocking took place in parametrized test generation, + // we do not need to process this [testSet] + if (TestCodeGeneratorPipeline.currentTestFrameworkConfiguration.isParametrizedAndMocked) { + conflictTriggers.reset(Conflict.ForceMockHappened, Conflict.ForceStaticMockHappened) + return + } + if (checkCoverageInCodeGenerationTests) { // TODO JIRA:1407 } @@ -2350,6 +2358,13 @@ abstract class UtValueTestCaseChecker( }" } + // if force mocking took place in parametrized test generation, + // we do not need to process this [testSet] + if (TestCodeGeneratorPipeline.currentTestFrameworkConfiguration.isParametrizedAndMocked) { + conflictTriggers.reset(Conflict.ForceMockHappened, Conflict.ForceStaticMockHappened) + return + } + val valueExecutions = valueTestCase.executions assertTrue(branches(valueExecutions.size)) { "Branch count matcher '$branches' fails for ${valueExecutions.size}: ${valueExecutions.prettify()}" @@ -2770,10 +2785,14 @@ inline fun withSettingsFromTestFrameworkConfiguration( ): T { val substituteStaticsWithSymbolicVariable = UtSettings.substituteStaticsWithSymbolicVariable UtSettings.substituteStaticsWithSymbolicVariable = config.resetNonFinalFieldsAfterClinit + + val previousConfig = TestCodeGeneratorPipeline.currentTestFrameworkConfiguration + TestCodeGeneratorPipeline.currentTestFrameworkConfiguration = config try { return block() } finally { UtSettings.substituteStaticsWithSymbolicVariable = substituteStaticsWithSymbolicVariable + TestCodeGeneratorPipeline.currentTestFrameworkConfiguration = previousConfig } } diff --git a/utbot-framework/src/test/kotlin/org/utbot/framework/codegen/TestCodeGeneratorPipeline.kt b/utbot-framework/src/test/kotlin/org/utbot/framework/codegen/TestCodeGeneratorPipeline.kt index f27a7d9507..95af9e2a8d 100644 --- a/utbot-framework/src/test/kotlin/org/utbot/framework/codegen/TestCodeGeneratorPipeline.kt +++ b/utbot-framework/src/test/kotlin/org/utbot/framework/codegen/TestCodeGeneratorPipeline.kt @@ -73,13 +73,9 @@ class TestCodeGeneratorPipeline(private val testFrameworkConfiguration: TestFram val codegenLanguage = testFrameworkConfiguration.codegenLanguage val parametrizedTestSource = testFrameworkConfiguration.parametrizedTestSource - val isParametrizedAndMocked = testFrameworkConfiguration.isParametrizedAndMocked val testClass = callToCodeGenerator(testSets, classUnderTest) - // clear triggered flags from the current launch in order to get ready for the next possible run - conflictTriggers.clear() - // actual number of the tests in the generated testClass val generatedMethodsCount = testClass .lines() @@ -101,11 +97,8 @@ class TestCodeGeneratorPipeline(private val testFrameworkConfiguration: TestFram trimmedLine.startsWith(prefix) } // expected number of the tests in the generated testClass - // if force mocking took place in parametrized test generation, - // we don't generate tests at all val expectedNumberOfGeneratedMethods = - if (isParametrizedAndMocked) 0 - else when (parametrizedTestSource) { + when (parametrizedTestSource) { ParametrizedTestSource.DO_NOT_PARAMETRIZE -> testSets.sumOf { it.executions.size } ParametrizedTestSource.PARAMETRIZE -> testSets.filter { it.executions.isNotEmpty() }.size } @@ -242,12 +235,7 @@ class TestCodeGeneratorPipeline(private val testFrameworkConfiguration: TestFram } val testClassCustomName = "${classUnderTest.java.simpleName}GeneratedTest" - // if force mocking took place in parametrized test generation, - // we don't generate tests at all by passing empty list instead of test sets - return codeGenerator.generateAsString( - if (testFrameworkConfiguration.isParametrizedAndMocked) listOf() else testSets, - testClassCustomName - ) + return codeGenerator.generateAsString(testSets, testClassCustomName) } private fun checkPipelinesResults(classesPipelines: List) { @@ -284,18 +272,17 @@ class TestCodeGeneratorPipeline(private val testFrameworkConfiguration: TestFram } companion object { - val CodegenLanguage.defaultCodegenPipeline: TestCodeGeneratorPipeline - get() = TestCodeGeneratorPipeline( - TestFrameworkConfiguration( - testFramework = TestFramework.defaultItem, - codegenLanguage = this, - mockFramework = MockFramework.defaultItem, - mockStrategy = MockStrategyApi.defaultItem, - staticsMocking = StaticsMocking.defaultItem, - parametrizedTestSource = ParametrizedTestSource.defaultItem, - forceStaticMocking = ForceStaticMocking.defaultItem, - ) - ) + var currentTestFrameworkConfiguration = defaultTestFrameworkConfiguration() + + fun defaultTestFrameworkConfiguration(language: CodegenLanguage = CodegenLanguage.JAVA) = TestFrameworkConfiguration( + testFramework = TestFramework.defaultItem, + codegenLanguage = language, + mockFramework = MockFramework.defaultItem, + mockStrategy = MockStrategyApi.defaultItem, + staticsMocking = StaticsMocking.defaultItem, + parametrizedTestSource = ParametrizedTestSource.defaultItem, + forceStaticMocking = ForceStaticMocking.defaultItem, + ) private const val ERROR_REGION_BEGINNING = "///region Errors" private const val ERROR_REGION_END = "///endregion"