Skip to content

Generate parametrized test for methods without force mocking in Uber and sample tests #591 #596 #608

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,16 @@ enum class Conflict {
}

class ConflictTriggers(
private val triggers: MutableMap<Conflict, Boolean> = EnumMap<Conflict, Boolean>(Conflict::class.java).withDefault { false }
): MutableMap<Conflict, Boolean> by triggers {
private val triggers: MutableMap<Conflict, Boolean> = EnumMap<Conflict, Boolean>(Conflict::class.java).also { map ->
Conflict.values().forEach { conflict -> map[conflict] = false }
}
) : MutableMap<Conflict, Boolean> by triggers {
val triggered: Boolean
get() = triggers.values.any { it }

fun reset(vararg conflicts: Conflict) {
for (conflict in conflicts) {
triggers[conflict] = false
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<*>,
Expand Down Expand Up @@ -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()}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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()}"
Expand Down Expand Up @@ -2770,10 +2785,14 @@ inline fun <reified T> 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
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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
}
Expand Down Expand Up @@ -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<ClassPipeline>) {
Expand Down Expand Up @@ -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"
Expand Down