diff --git a/utbot-cli/src/main/kotlin/org/utbot/cli/GenerateTestsAbstractCommand.kt b/utbot-cli/src/main/kotlin/org/utbot/cli/GenerateTestsAbstractCommand.kt index 0dbf64819b..4d39fd2c1c 100644 --- a/utbot-cli/src/main/kotlin/org/utbot/cli/GenerateTestsAbstractCommand.kt +++ b/utbot-cli/src/main/kotlin/org/utbot/cli/GenerateTestsAbstractCommand.kt @@ -23,6 +23,7 @@ import org.utbot.framework.codegen.domain.ProjectType import org.utbot.framework.codegen.domain.StaticsMocking import org.utbot.framework.codegen.domain.testFrameworkByName import org.utbot.framework.codegen.generator.CodeGenerator +import org.utbot.framework.codegen.generator.CodeGeneratorParams import org.utbot.framework.codegen.services.language.CgLanguageAssistant import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.CodegenLanguage @@ -210,15 +211,17 @@ abstract class GenerateTestsAbstractCommand(name: String, help: String) : val generateWarningsForStaticMocking = forceStaticMocking == ForceStaticMocking.FORCE && staticsMocking is NoStaticMocking return CodeGenerator( - testFramework = testFrameworkByName(testFramework), - classUnderTest = classUnderTest, - //TODO: Support Spring projects in utbot-cli if requested - projectType = ProjectType.PureJvm, - codegenLanguage = codegenLanguage, - cgLanguageAssistant = CgLanguageAssistant.getByCodegenLanguage(codegenLanguage), - staticsMocking = staticsMocking, - forceStaticMocking = forceStaticMocking, - generateWarningsForStaticMocking = generateWarningsForStaticMocking, + CodeGeneratorParams( + testFramework = testFrameworkByName(testFramework), + classUnderTest = classUnderTest, + //TODO: Support Spring projects in utbot-cli if requested + projectType = ProjectType.PureJvm, + codegenLanguage = codegenLanguage, + cgLanguageAssistant = CgLanguageAssistant.getByCodegenLanguage(codegenLanguage), + staticsMocking = staticsMocking, + forceStaticMocking = forceStaticMocking, + generateWarningsForStaticMocking = generateWarningsForStaticMocking, + ) ) } diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt index 80f0f070d2..9ff4eebed3 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt @@ -56,17 +56,8 @@ import java.io.File import kotlin.contracts.ExperimentalContracts import kotlin.contracts.contract import org.utbot.common.isAbstract -import org.utbot.common.isStatic -import org.utbot.framework.isFromTrustedLibrary -import org.utbot.framework.plugin.api.TypeReplacementMode.* import org.utbot.framework.plugin.api.util.SpringModelUtils -import org.utbot.framework.plugin.api.util.allDeclaredFieldIds -import org.utbot.framework.plugin.api.util.allSuperTypes -import org.utbot.framework.plugin.api.util.fieldId -import org.utbot.framework.plugin.api.util.isSubtypeOf -import org.utbot.framework.plugin.api.util.utContext import org.utbot.framework.process.OpenModulesContainer -import soot.SootField import soot.SootMethod const val SYMBOLIC_NULL_ADDR: Int = 0 @@ -1328,241 +1319,54 @@ enum class TypeReplacementMode { NoImplementors, } -interface CodeGenerationContext - -interface SpringCodeGenerationContext : CodeGenerationContext { - val springTestType: SpringTestType - val springSettings: SpringSettings - val springContextLoadingResult: SpringContextLoadingResult? -} - -/** - * A context to use when no specific data is required. - * - * @param mockFrameworkInstalled shows if we have installed framework dependencies - * @param staticsMockingIsConfigured shows if we have installed static mocking tools - */ -open class ApplicationContext( - val mockFrameworkInstalled: Boolean = true, - staticsMockingIsConfigured: Boolean = true, -) : CodeGenerationContext { - var staticsMockingIsConfigured = staticsMockingIsConfigured - private set - - init { - /** - * Situation when mock framework is not installed but static mocking is configured is semantically incorrect. - * - * However, it may be obtained in real application after this actions: - * - fully configure mocking (dependency installed + resource file created) - * - remove mockito-core dependency from project - * - forget to remove mock-maker file from resource directory - * - * Here we transform this configuration to semantically correct. - */ - if (!mockFrameworkInstalled && staticsMockingIsConfigured) { - this.staticsMockingIsConfigured = false - } - } - - /** - * Shows if there are any restrictions on type implementors. - */ - open val typeReplacementMode: TypeReplacementMode = AnyImplementor - - /** - * Finds a type to replace the original abstract type - * if it is guided with some additional information. - */ - open fun replaceTypeIfNeeded(type: RefType): ClassId? = null - - /** - * Sets the restrictions on speculative not null - * constraints in current application context. - * - * @see docs/SpeculativeFieldNonNullability.md for more information. - */ - open fun avoidSpeculativeNotNullChecks(field: SootField): Boolean = - UtSettings.maximizeCoverageUsingReflection || !field.declaringClass.isFromTrustedLibrary() - - /** - * Checks whether accessing [field] (with a method invocation or field access) speculatively - * cannot produce [NullPointerException] (according to its finality or accessibility). - * - * @see docs/SpeculativeFieldNonNullability.md for more information. - */ - open fun speculativelyCannotProduceNullPointerException( - field: SootField, - classUnderTest: ClassId, - ): Boolean = field.isFinal || !field.isPublic - - open fun preventsFurtherTestGeneration(): Boolean = false - - open fun getErrors(): List = emptyList() -} - sealed class SpringConfiguration(val fullDisplayName: String) { class JavaConfiguration(val classBinaryName: String) : SpringConfiguration(classBinaryName) class XMLConfiguration(val absolutePath: String) : SpringConfiguration(absolutePath) } sealed interface SpringSettings { - class AbsentSpringSettings : SpringSettings { - // Denotes no configuration and no profile setting - - // NOTICE: - // `class` should not be replaced with `object` - // in order to avoid issues caused by Kryo deserialization - // that creates new instances breaking `when` expressions - // that check reference equality instead of type equality + object AbsentSpringSettings : SpringSettings { + // NOTE that overriding equals is required just because without it + // we will lose equality for objects after deserialization + override fun equals(other: Any?): Boolean = other is AbsentSpringSettings + + override fun hashCode(): Int = 0 } - class PresentSpringSettings( + data class PresentSpringSettings( val configuration: SpringConfiguration, - val profiles: Array + val profiles: List ) : SpringSettings } /** - * [contextLoaded] can be `true` while [exceptions] is not empty, - * if we failed to use most specific SpringApi available (e.g. SpringBoot), but - * were able to successfully fall back to less specific SpringApi (e.g. PureSpring). + * Result of loading concrete execution context (e.g. Spring application context). + * + * [contextLoaded] can be `true` while [exceptions] is not empty. For example, we may fail + * to load context with most specific SpringApi available (e.g. SpringBoot), + * but successfully fall back to less specific SpringApi (e.g. PureSpring). */ -class SpringContextLoadingResult( +class ConcreteContextLoadingResult( val contextLoaded: Boolean, val exceptions: List -) - -/** - * Data we get from Spring application context - * to manage engine and code generator behaviour. - * - * @param beanDefinitions describes bean definitions (bean name, type, some optional additional data) - * @param shouldUseImplementors describes it we want to replace interfaces with injected types or not - */ -// TODO move this class to utbot-framework so we can use it as abstract factory -// to get rid of numerous `when`s and polymorphically create things like: -// - Instrumentation -// - FuzzedType (to get rid of thisInstanceFuzzedTypeWrapper) -// - JavaValueProvider -// - CgVariableConstructor -// - CodeGeneratorResult (generateForSpringClass) -// Right now this refactoring is blocked because some interfaces need to get extracted and moved to utbot-framework-api -// As an alternative we can just move ApplicationContext itself to utbot-framework -class SpringApplicationContext( - mockInstalled: Boolean, - staticsMockingIsConfigured: Boolean, - val beanDefinitions: List = emptyList(), - private val shouldUseImplementors: Boolean, - override val springTestType: SpringTestType, - override val springSettings: SpringSettings, -): ApplicationContext(mockInstalled, staticsMockingIsConfigured), SpringCodeGenerationContext { - - override var springContextLoadingResult: SpringContextLoadingResult? = null +) { + val utErrors: List get() = + exceptions.map { UtError(it.message ?: "Concrete context loading failed", it) } + + fun andThen(onSuccess: () -> ConcreteContextLoadingResult) = + if (contextLoaded) { + val otherResult = onSuccess() + ConcreteContextLoadingResult( + contextLoaded = otherResult.contextLoaded, + exceptions = exceptions + otherResult.exceptions + ) + } else this companion object { - private val logger = KotlinLogging.logger {} - } - - private var areInjectedClassesInitialized : Boolean = false - private var areAllInjectedTypesInitialized: Boolean = false - - // Classes representing concrete types that are actually used in Spring application - private val springInjectedClasses: Set - get() { - if (!areInjectedClassesInitialized) { - for (beanTypeName in beanDefinitions.map { it.beanTypeName }) { - try { - val beanClass = utContext.classLoader.loadClass(beanTypeName) - if (!beanClass.isAbstract && !beanClass.isInterface && - !beanClass.isLocalClass && (!beanClass.isMemberClass || beanClass.isStatic)) { - springInjectedClassesStorage += beanClass.id - } - } catch (e: Throwable) { - // For some Spring beans (e.g. with anonymous classes) - // it is possible to have problems with classes loading. - when (e) { - is ClassNotFoundException, is NoClassDefFoundError, is IllegalAccessError -> - logger.warn { "Failed to load bean class for $beanTypeName (${e.message})" } - - else -> throw e - } - } - } - - // This is done to be sure that this storage is not empty after the first class loading iteration. - // So, even if all loaded classes were filtered out, we will not try to load them again. - areInjectedClassesInitialized = true - } - - return springInjectedClassesStorage - } - - private val allInjectedTypes: Set - get() { - if (!areAllInjectedTypesInitialized) { - allInjectedTypesStorage = springInjectedClasses.flatMap { it.allSuperTypes() }.toSet() - areAllInjectedTypesInitialized = true - } - - return allInjectedTypesStorage - } - - // imitates `by lazy` (we can't use actual `by lazy` because communication via RD breaks it) - private var allInjectedTypesStorage: Set = emptySet() - - // This is a service field to model the lazy behavior of [springInjectedClasses]. - // Do not call it outside the getter. - // - // Actually, we should just call [springInjectedClasses] with `by lazy`, but we had problems - // with a strange `kotlin.UNINITIALIZED_VALUE` in `speculativelyCannotProduceNullPointerException` method call. - private val springInjectedClassesStorage = mutableSetOf() - - override val typeReplacementMode: TypeReplacementMode = - if (shouldUseImplementors) KnownImplementor else NoImplementors - - /** - * Replaces an interface type with its implementor type - * if there is the unique implementor in bean definitions. - */ - override fun replaceTypeIfNeeded(type: RefType): ClassId? = - if (type.isAbstractType) { - springInjectedClasses.singleOrNull { it.isSubtypeOf(type.id) } - } else { - null - } - - override fun avoidSpeculativeNotNullChecks(field: SootField): Boolean = false - - /** - * In Spring applications we can mark as speculatively not null - * fields if they are mocked and injecting into class under test so on. - * - * Fields are not mocked if their actual type is obtained from [springInjectedClasses]. - * - */ - override fun speculativelyCannotProduceNullPointerException( - field: SootField, - classUnderTest: ClassId, - ): Boolean = field.fieldId in classUnderTest.allDeclaredFieldIds && field.type.classId !in allInjectedTypes - - override fun preventsFurtherTestGeneration(): Boolean = - super.preventsFurtherTestGeneration() || springContextLoadingResult?.contextLoaded == false - - override fun getErrors(): List = - springContextLoadingResult?.exceptions?.map { exception -> - UtError( - "Failed to load Spring application context", - exception - ) - }.orEmpty() + super.getErrors() - - fun getBeansAssignableTo(classId: ClassId): List = beanDefinitions.filter { beanDef -> - // some bean classes may fail to load - runCatching { - val beanClass = ClassId(beanDef.beanTypeName).jClass - classId.jClass.isAssignableFrom(beanClass) - }.getOrElse { false } + fun successWithoutExceptions() = ConcreteContextLoadingResult( + contextLoaded = true, + exceptions = emptyList() + ) } } diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/Mocks.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/Mocks.kt index 4b16c57db4..9faf511ab6 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Mocks.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Mocks.kt @@ -19,7 +19,7 @@ import kotlinx.collections.immutable.persistentListOf import org.utbot.common.nameOfPackage import org.utbot.engine.types.OBJECT_TYPE import org.utbot.engine.util.mockListeners.MockListenerController -import org.utbot.framework.plugin.api.ApplicationContext +import org.utbot.framework.context.MockerContext import org.utbot.framework.plugin.api.util.isInaccessibleViaReflection import soot.BooleanType import soot.RefType @@ -168,7 +168,7 @@ class Mocker( private val hierarchy: Hierarchy, chosenClassesToMockAlways: Set, internal val mockListenerController: MockListenerController? = null, - private val applicationContext: ApplicationContext, + private val mockerContext: MockerContext, ) { private val mocksAreDesired: Boolean = strategy != MockStrategy.NO_MOCKS @@ -227,10 +227,10 @@ class Mocker( val mockingIsPossible = when (mockInfo) { is UtFieldMockInfo, - is UtObjectMockInfo -> applicationContext.mockFrameworkInstalled + is UtObjectMockInfo -> mockerContext.mockFrameworkInstalled is UtNewInstanceMockInfo, is UtStaticMethodMockInfo, - is UtStaticObjectMockInfo -> applicationContext.staticsMockingIsConfigured + is UtStaticObjectMockInfo -> mockerContext.staticsMockingIsConfigured } val mockingIsForcedAndPossible = mockAlways(mockedValue.type) && mockingIsPossible diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt index fc213b52de..38c7e71d65 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt @@ -116,7 +116,9 @@ import org.utbot.framework.UtSettings import org.utbot.framework.UtSettings.preferredCexOption import org.utbot.framework.UtSettings.substituteStaticsWithSymbolicVariable import org.utbot.framework.isFromTrustedLibrary -import org.utbot.framework.plugin.api.ApplicationContext +import org.utbot.framework.context.ApplicationContext +import org.utbot.framework.context.NonNullSpeculator +import org.utbot.framework.context.TypeReplacer import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.ExecutableId import org.utbot.framework.plugin.api.FieldId @@ -239,7 +241,8 @@ class Traverser( internal val typeResolver: TypeResolver, private val globalGraph: InterProceduralUnitGraph, private val mocker: Mocker, - private val applicationContext: ApplicationContext, + private val typeReplacer: TypeReplacer, + private val nonNullSpeculator: NonNullSpeculator, private val taintContext: TaintContext, ) : UtContextInitializer() { @@ -1393,8 +1396,8 @@ class Traverser( // However, if we have the restriction on implementor type (it may be obtained // from Spring bean definitions, for example), we can just create a symbolic object // with hard constraint on the mentioned type. - val replacedClassId = when (applicationContext.typeReplacementMode) { - KnownImplementor -> applicationContext.replaceTypeIfNeeded(type) + val replacedClassId = when (typeReplacer.typeReplacementMode) { + KnownImplementor -> typeReplacer.replaceTypeIfNeeded(type) AnyImplementor, NoImplementors -> null } @@ -1512,7 +1515,7 @@ class Traverser( return createMockedObject(addr, type, mockInfoGenerator, nullEqualityConstraint) } - val concreteImplementation: Concrete? = when (applicationContext.typeReplacementMode) { + val concreteImplementation: Concrete? = when (typeReplacer.typeReplacementMode) { AnyImplementor -> findConcreteImplementation(addr, type, typeHardConstraint) // If our type is not abstract, both in `KnownImplementors` and `NoImplementors` mode, @@ -2336,12 +2339,8 @@ class Traverser( * See more detailed documentation in [ApplicationContext] mentioned methods. */ private fun checkAndMarkLibraryFieldSpeculativelyNotNull(field: SootField, createdField: SymbolicValue) { - if (applicationContext.avoidSpeculativeNotNullChecks(field) || - !applicationContext.speculativelyCannotProduceNullPointerException(field, methodUnderTest.classId)) { - return - } - - markAsSpeculativelyNotNull(createdField.addr) + if (nonNullSpeculator.speculativelyCannotProduceNullPointerException(field, methodUnderTest.classId)) + markAsSpeculativelyNotNull(createdField.addr) } private fun createArray(pName: String, type: ArrayType): ArrayValue { diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt index 43077551f2..46d16f234d 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt @@ -10,7 +10,6 @@ import org.utbot.analytics.Predictors import org.utbot.api.exception.UtMockAssumptionViolatedException import org.utbot.common.debug import org.utbot.common.measureTime -import org.utbot.common.tryLoadClass import org.utbot.engine.MockStrategy.NO_MOCKS import org.utbot.engine.pc.* import org.utbot.engine.selectors.* @@ -33,6 +32,8 @@ import org.utbot.framework.UtSettings.pathSelectorStepsLimit import org.utbot.framework.UtSettings.pathSelectorType import org.utbot.framework.UtSettings.processUnknownStatesDuringConcreteExecution import org.utbot.framework.UtSettings.useDebugVisualization +import org.utbot.framework.context.ApplicationContext +import org.utbot.framework.context.ConcreteExecutionContext import org.utbot.framework.plugin.api.* import org.utbot.framework.plugin.api.Step import org.utbot.framework.plugin.api.util.* @@ -42,13 +43,8 @@ import org.utbot.framework.util.graph import org.utbot.framework.util.sootMethod import org.utbot.fuzzer.* import org.utbot.fuzzing.* -import org.utbot.fuzzing.providers.FieldValueProvider -import org.utbot.fuzzing.providers.ObjectValueProvider -import org.utbot.fuzzing.spring.SavedEntityValueProvider -import org.utbot.fuzzing.spring.SpringBeanValueProvider import org.utbot.fuzzing.utils.Trie import org.utbot.instrumentation.ConcreteExecutor -import org.utbot.instrumentation.getRelevantSpringRepositories import org.utbot.instrumentation.instrumentation.Instrumentation import org.utbot.instrumentation.instrumentation.execution.UtConcreteExecutionData import org.utbot.instrumentation.instrumentation.execution.UtConcreteExecutionResult @@ -115,10 +111,11 @@ class UtBotSymbolicEngine( val mockStrategy: MockStrategy = NO_MOCKS, chosenClassesToMockAlways: Set, val applicationContext: ApplicationContext, - executionInstrumentation: Instrumentation, + val concreteExecutionContext: ConcreteExecutionContext, userTaintConfigurationProvider: TaintConfigurationProvider? = null, private val solverTimeoutInMillis: Int = checkSolverTimeoutMillis, ) : UtContextInitializer() { + private val graph = methodUnderTest.sootMethod.jimpleBody().apply { logger.trace { "JIMPLE for $methodUnderTest:\n$this" } }.graph() @@ -141,7 +138,7 @@ class UtBotSymbolicEngine( hierarchy, chosenClassesToMockAlways, MockListenerController(controller), - applicationContext = applicationContext, + mockerContext = applicationContext.mockerContext, ) fun attachMockListener(mockListener: MockListener) = mocker.mockListenerController?.attach(mockListener) @@ -177,7 +174,8 @@ class UtBotSymbolicEngine( typeResolver, globalGraph, mocker, - applicationContext, + applicationContext.typeReplacer, + applicationContext.nonNullSpeculator, taintContext, ) @@ -186,7 +184,7 @@ class UtBotSymbolicEngine( private val concreteExecutor = ConcreteExecutor( - executionInstrumentation, + concreteExecutionContext.instrumentationFactory, classpath, ).apply { this.classLoader = utContext.classLoader } @@ -388,57 +386,15 @@ class UtBotSymbolicEngine( val names = graph.body.method.tags.filterIsInstance().firstOrNull()?.names ?: emptyList() var testEmittedByFuzzer = 0 - if (applicationContext is SpringApplicationContext && - applicationContext.springTestType == SpringTestType.INTEGRATION_TEST && - applicationContext.getBeansAssignableTo(methodUnderTest.classId).isEmpty()) { - val fullConfigDisplayName = (applicationContext.springSettings as? SpringSettings.PresentSpringSettings) - ?.configuration?.fullDisplayName - val errorDescription = "No beans of type ${methodUnderTest.classId.name} were found. " + - "Try choosing different Spring configuration or adding beans to $fullConfigDisplayName" - emit(UtError( - errorDescription, - IllegalStateException(errorDescription) - )) + val valueProviders = try { + concreteExecutionContext.tryCreateValueProvider(concreteExecutor, classUnderTest, defaultIdGenerator) + } catch (e: Exception) { + emit(UtError(e.message ?: "Failed to create ValueProvider", e)) return@flow - } - - val valueProviders = ValueProvider.of(defaultValueProviders(defaultIdGenerator)) - .letIf(applicationContext is SpringApplicationContext - && applicationContext.springTestType == SpringTestType.INTEGRATION_TEST - ) { provider -> - val relevantRepositories = concreteExecutor.getRelevantSpringRepositories(methodUnderTest.classId) - logger.info { "Detected relevant repositories for class ${methodUnderTest.classId}: $relevantRepositories" } + }.let(transform) - val generatedValueAnnotationClasses = SpringModelUtils.generatedValueClassIds.mapNotNull { - @Suppress("UNCHECKED_CAST") // type system fails to understand that GeneratedValue is indeed an annotation - utContext.classLoader.tryLoadClass(it.name) as Class? - } - - val generatedValueFieldIds = - relevantRepositories - .map { it.entityClassId.jClass } - .flatMap { entityClass -> generateSequence(entityClass) { it.superclass } } - .flatMap { it.declaredFields.toList() } - .filter { field -> generatedValueAnnotationClasses.any { field.isAnnotationPresent(it) } } - .map { it.fieldId } - logger.info { "Detected @GeneratedValue fields: $generatedValueFieldIds" } - - // spring should try to generate bean values, but if it fails, then object value provider is used for it - val springBeanValueProvider = SpringBeanValueProvider( - defaultIdGenerator, - beanNameProvider = { classId -> - (applicationContext as SpringApplicationContext).getBeansAssignableTo(classId) - .map { it.beanName } - }, - relevantRepositories = relevantRepositories - ).withFallback(ObjectValueProvider(defaultIdGenerator)) - provider - .except { p -> p is ObjectValueProvider } - .with(springBeanValueProvider) - .with(ValueProvider.of(relevantRepositories.map { SavedEntityValueProvider(defaultIdGenerator, it) })) - .with(ValueProvider.of(generatedValueFieldIds.map { FieldValueProvider(defaultIdGenerator, it) })) - }.let(transform) val coverageToMinStateBeforeSize = mutableMapOf, Int>() + runJavaFuzzing( defaultIdGenerator, methodUnderTest, diff --git a/utbot-framework/src/main/kotlin/org/utbot/external/api/UtBotJavaApi.kt b/utbot-framework/src/main/kotlin/org/utbot/external/api/UtBotJavaApi.kt index 1036f927ac..2168d67471 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/external/api/UtBotJavaApi.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/external/api/UtBotJavaApi.kt @@ -10,6 +10,7 @@ import org.utbot.framework.codegen.domain.ProjectType import org.utbot.framework.codegen.domain.StaticsMocking import org.utbot.framework.codegen.domain.TestFramework import org.utbot.framework.codegen.generator.CodeGenerator +import org.utbot.framework.codegen.generator.CodeGeneratorParams import org.utbot.framework.codegen.services.language.CgLanguageAssistant import org.utbot.instrumentation.instrumentation.execution.UtConcreteExecutionData import org.utbot.instrumentation.instrumentation.execution.UtConcreteExecutionResult @@ -40,6 +41,8 @@ import org.utbot.fuzzing.Seed import org.utbot.fuzzing.ValueProvider import org.utbot.instrumentation.ConcreteExecutor import org.utbot.instrumentation.execute +import org.utbot.instrumentation.instrumentation.execution.SimpleUtExecutionInstrumentation +import java.io.File import kotlin.reflect.jvm.kotlinFunction object UtBotJavaApi { @@ -71,7 +74,7 @@ object UtBotJavaApi { val testSets: MutableList = generatedTestCases.toMutableList() val concreteExecutor = ConcreteExecutor( - UtExecutionInstrumentation, + SimpleUtExecutionInstrumentation.Factory(pathsToUserClasses = classpath.split(File.pathSeparator).toSet()), classpath, ) @@ -83,6 +86,7 @@ object UtBotJavaApi { return withUtContext(utContext) { val codeGenerator = CodeGenerator( + CodeGeneratorParams( classUnderTest = classUnderTest.id, projectType = projectType, testFramework = testFramework, @@ -94,6 +98,7 @@ object UtBotJavaApi { generateWarningsForStaticMocking = generateWarningsForStaticMocking, testClassPackageName = testClassPackageName ) + ) codeGenerator.generateAsString(testSets, destinationClassName) } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/generator/AbstractCodeGenerator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/generator/AbstractCodeGenerator.kt index dca05eed0e..a7a05831f5 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/generator/AbstractCodeGenerator.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/generator/AbstractCodeGenerator.kt @@ -1,64 +1,37 @@ package org.utbot.framework.codegen.generator import mu.KotlinLogging -import org.utbot.framework.codegen.domain.ForceStaticMocking -import org.utbot.framework.codegen.domain.HangingTestsTimeout -import org.utbot.framework.codegen.domain.ParametrizedTestSource -import org.utbot.framework.codegen.domain.ProjectType -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.context.CgContext import org.utbot.framework.codegen.domain.models.CgClassFile import org.utbot.framework.codegen.domain.models.CgMethodTestSet import org.utbot.framework.codegen.renderer.CgAbstractRenderer -import org.utbot.framework.codegen.services.language.CgLanguageAssistant -import org.utbot.framework.plugin.api.ClassId -import org.utbot.framework.plugin.api.CodegenLanguage -import org.utbot.framework.plugin.api.ExecutableId -import org.utbot.framework.plugin.api.MockFramework import org.utbot.framework.plugin.api.UtMethodTestSet import java.time.LocalDateTime import java.time.format.DateTimeFormatter -abstract class AbstractCodeGenerator( - classUnderTest: ClassId, - projectType: ProjectType, - paramNames: MutableMap> = mutableMapOf(), - generateUtilClassFile: Boolean = false, - testFramework: TestFramework = TestFramework.defaultItem, - mockFramework: MockFramework = MockFramework.defaultItem, - staticsMocking: StaticsMocking = StaticsMocking.defaultItem, - forceStaticMocking: ForceStaticMocking = ForceStaticMocking.defaultItem, - generateWarningsForStaticMocking: Boolean = true, - codegenLanguage: CodegenLanguage = CodegenLanguage.defaultItem, - cgLanguageAssistant: CgLanguageAssistant = CgLanguageAssistant.getByCodegenLanguage(codegenLanguage), - parameterizedTestSource: ParametrizedTestSource = ParametrizedTestSource.defaultItem, - runtimeExceptionTestsBehaviour: RuntimeExceptionTestsBehaviour = RuntimeExceptionTestsBehaviour.defaultItem, - hangingTestsTimeout: HangingTestsTimeout = HangingTestsTimeout(), - enableTestsTimeout: Boolean = true, - testClassPackageName: String = classUnderTest.packageName, -) { +abstract class AbstractCodeGenerator(params: CodeGeneratorParams) { protected val logger = KotlinLogging.logger {} - open var context: CgContext = CgContext( - classUnderTest = classUnderTest, - projectType = projectType, - generateUtilClassFile = generateUtilClassFile, - paramNames = paramNames, - testFramework = testFramework, - mockFramework = mockFramework, - codegenLanguage = codegenLanguage, - cgLanguageAssistant = cgLanguageAssistant, - parametrizedTestSource = parameterizedTestSource, - staticsMocking = staticsMocking, - forceStaticMocking = forceStaticMocking, - generateWarningsForStaticMocking = generateWarningsForStaticMocking, - runtimeExceptionTestsBehaviour = runtimeExceptionTestsBehaviour, - hangingTestsTimeout = hangingTestsTimeout, - enableTestsTimeout = enableTestsTimeout, - testClassPackageName = testClassPackageName - ) + open var context: CgContext = with(params) { + CgContext( + classUnderTest = classUnderTest, + projectType = projectType, + generateUtilClassFile = generateUtilClassFile, + paramNames = paramNames, + testFramework = testFramework, + mockFramework = mockFramework, + codegenLanguage = codegenLanguage, + cgLanguageAssistant = cgLanguageAssistant, + parametrizedTestSource = parameterizedTestSource, + staticsMocking = staticsMocking, + forceStaticMocking = forceStaticMocking, + generateWarningsForStaticMocking = generateWarningsForStaticMocking, + runtimeExceptionTestsBehaviour = runtimeExceptionTestsBehaviour, + hangingTestsTimeout = hangingTestsTimeout, + enableTestsTimeout = enableTestsTimeout, + testClassPackageName = testClassPackageName + ) + } //TODO: we support custom test class name only in utbot-online, probably support them in plugin as well fun generateAsString(testSets: Collection, testClassCustomName: String? = null): String = 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 8b4b75f70c..5dc79b10e5 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 @@ -1,57 +1,13 @@ package org.utbot.framework.codegen.generator -import org.utbot.framework.codegen.domain.ForceStaticMocking -import org.utbot.framework.codegen.domain.HangingTestsTimeout -import org.utbot.framework.codegen.domain.ParametrizedTestSource -import org.utbot.framework.codegen.domain.ProjectType -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.builders.SimpleTestClassModelBuilder -import org.utbot.framework.codegen.services.language.CgLanguageAssistant import org.utbot.framework.codegen.tree.CgSimpleTestClassConstructor import org.utbot.framework.codegen.tree.ututils.UtilClassKind import org.utbot.framework.plugin.api.ClassId -import org.utbot.framework.plugin.api.CodegenLanguage -import org.utbot.framework.plugin.api.ExecutableId -import org.utbot.framework.plugin.api.MockFramework -open class CodeGenerator( - val classUnderTest: ClassId, - val projectType: ProjectType, - paramNames: MutableMap> = mutableMapOf(), - generateUtilClassFile: Boolean = false, - testFramework: TestFramework = TestFramework.defaultItem, - mockFramework: MockFramework = MockFramework.defaultItem, - staticsMocking: StaticsMocking = StaticsMocking.defaultItem, - forceStaticMocking: ForceStaticMocking = ForceStaticMocking.defaultItem, - generateWarningsForStaticMocking: Boolean = true, - codegenLanguage: CodegenLanguage = CodegenLanguage.defaultItem, - cgLanguageAssistant: CgLanguageAssistant = CgLanguageAssistant.getByCodegenLanguage(codegenLanguage), - parameterizedTestSource: ParametrizedTestSource = ParametrizedTestSource.defaultItem, - runtimeExceptionTestsBehaviour: RuntimeExceptionTestsBehaviour = RuntimeExceptionTestsBehaviour.defaultItem, - hangingTestsTimeout: HangingTestsTimeout = HangingTestsTimeout(), - enableTestsTimeout: Boolean = true, - testClassPackageName: String = classUnderTest.packageName, -): AbstractCodeGenerator( - classUnderTest, - projectType, - paramNames, - generateUtilClassFile, - testFramework, - mockFramework, - staticsMocking, - forceStaticMocking, - generateWarningsForStaticMocking, - codegenLanguage, - cgLanguageAssistant, - parameterizedTestSource, - runtimeExceptionTestsBehaviour, - hangingTestsTimeout, - enableTestsTimeout, - testClassPackageName, -) { +open class CodeGenerator(params: CodeGeneratorParams): AbstractCodeGenerator(params) { + protected val classUnderTest: ClassId = params.classUnderTest override fun generate(testSets: List): CodeGeneratorResult { val testClassModel = SimpleTestClassModelBuilder(context).createTestClassModel(classUnderTest, testSets) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/generator/CodeGeneratorParams.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/generator/CodeGeneratorParams.kt new file mode 100644 index 0000000000..5bbc205307 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/generator/CodeGeneratorParams.kt @@ -0,0 +1,33 @@ +package org.utbot.framework.codegen.generator + +import org.utbot.framework.codegen.domain.ForceStaticMocking +import org.utbot.framework.codegen.domain.HangingTestsTimeout +import org.utbot.framework.codegen.domain.ParametrizedTestSource +import org.utbot.framework.codegen.domain.ProjectType +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.services.language.CgLanguageAssistant +import org.utbot.framework.plugin.api.ClassId +import org.utbot.framework.plugin.api.CodegenLanguage +import org.utbot.framework.plugin.api.ExecutableId +import org.utbot.framework.plugin.api.MockFramework + +data class CodeGeneratorParams( + val classUnderTest: ClassId, + val projectType: ProjectType, + val paramNames: MutableMap> = mutableMapOf(), + val generateUtilClassFile: Boolean = false, + val testFramework: TestFramework = TestFramework.defaultItem, + val mockFramework: MockFramework = MockFramework.defaultItem, + val staticsMocking: StaticsMocking = StaticsMocking.defaultItem, + val forceStaticMocking: ForceStaticMocking = ForceStaticMocking.defaultItem, + val generateWarningsForStaticMocking: Boolean = true, + val codegenLanguage: CodegenLanguage = CodegenLanguage.defaultItem, + val cgLanguageAssistant: CgLanguageAssistant = CgLanguageAssistant.getByCodegenLanguage(codegenLanguage), + val parameterizedTestSource: ParametrizedTestSource = ParametrizedTestSource.defaultItem, + val runtimeExceptionTestsBehaviour: RuntimeExceptionTestsBehaviour = RuntimeExceptionTestsBehaviour.defaultItem, + val hangingTestsTimeout: HangingTestsTimeout = HangingTestsTimeout(), + val enableTestsTimeout: Boolean = true, + val testClassPackageName: String = classUnderTest.packageName, +) \ No newline at end of file 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 0fa4c449b1..937c841a26 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 @@ -1,71 +1,46 @@ package org.utbot.framework.codegen.generator -import org.utbot.framework.codegen.domain.ForceStaticMocking -import org.utbot.framework.codegen.domain.HangingTestsTimeout -import org.utbot.framework.codegen.domain.ParametrizedTestSource -import org.utbot.framework.codegen.domain.ProjectType -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.context.CgContext import org.utbot.framework.codegen.domain.models.CgMethodTestSet import org.utbot.framework.codegen.domain.models.builders.SpringTestClassModelBuilder import org.utbot.framework.codegen.services.language.CgLanguageAssistant import org.utbot.framework.codegen.tree.CgSpringIntegrationTestClassConstructor import org.utbot.framework.codegen.tree.CgSpringUnitTestClassConstructor +import org.utbot.framework.codegen.tree.CgSpringVariableConstructor +import org.utbot.framework.codegen.tree.CgVariableConstructor import org.utbot.framework.codegen.tree.ututils.UtilClassKind import org.utbot.framework.plugin.api.ClassId -import org.utbot.framework.plugin.api.CodegenLanguage -import org.utbot.framework.plugin.api.ExecutableId -import org.utbot.framework.plugin.api.MockFramework +import org.utbot.framework.plugin.api.ConcreteContextLoadingResult +import org.utbot.framework.plugin.api.SpringSettings +import org.utbot.framework.plugin.api.SpringSettings.AbsentSpringSettings +import org.utbot.framework.plugin.api.SpringSettings.PresentSpringSettings import org.utbot.framework.plugin.api.SpringTestType -import org.utbot.framework.plugin.api.SpringCodeGenerationContext -import org.utbot.framework.plugin.api.SpringSettings.* class SpringCodeGenerator( - val classUnderTest: ClassId, - val projectType: ProjectType, - val springCodeGenerationContext: SpringCodeGenerationContext, - paramNames: MutableMap> = mutableMapOf(), - generateUtilClassFile: Boolean = false, - testFramework: TestFramework = TestFramework.defaultItem, - mockFramework: MockFramework = MockFramework.defaultItem, - staticsMocking: StaticsMocking = StaticsMocking.defaultItem, - forceStaticMocking: ForceStaticMocking = ForceStaticMocking.defaultItem, - generateWarningsForStaticMocking: Boolean = true, - codegenLanguage: CodegenLanguage = CodegenLanguage.defaultItem, - cgLanguageAssistant: CgLanguageAssistant = CgLanguageAssistant.getByCodegenLanguage(codegenLanguage), - parameterizedTestSource: ParametrizedTestSource = ParametrizedTestSource.defaultItem, - runtimeExceptionTestsBehaviour: RuntimeExceptionTestsBehaviour = RuntimeExceptionTestsBehaviour.defaultItem, - hangingTestsTimeout: HangingTestsTimeout = HangingTestsTimeout(), - enableTestsTimeout: Boolean = true, - testClassPackageName: String = classUnderTest.packageName, + private val springTestType: SpringTestType, + private val springSettings: SpringSettings, + private val concreteContextLoadingResult: ConcreteContextLoadingResult?, + params: CodeGeneratorParams ) : AbstractCodeGenerator( - classUnderTest, - projectType, - paramNames, - generateUtilClassFile, - testFramework, - mockFramework, - staticsMocking, - forceStaticMocking, - generateWarningsForStaticMocking, - codegenLanguage, - cgLanguageAssistant, - parameterizedTestSource, - runtimeExceptionTestsBehaviour, - hangingTestsTimeout, - enableTestsTimeout, - testClassPackageName, + params.copy( + cgLanguageAssistant = object : CgLanguageAssistant by params.cgLanguageAssistant { + override fun getVariableConstructorBy(context: CgContext): CgVariableConstructor = + // TODO decorate original `params.cgLanguageAssistant.getVariableConstructorBy(context)` + CgSpringVariableConstructor(context) + } + ) ) { + private val classUnderTest: ClassId = params.classUnderTest + override fun generate(testSets: List): CodeGeneratorResult { val testClassModel = SpringTestClassModelBuilder(context).createTestClassModel(classUnderTest, testSets) logger.info { "Code generation phase started at ${now()}" } - val astConstructor = when (springCodeGenerationContext.springTestType) { + val astConstructor = when (springTestType) { SpringTestType.UNIT_TEST -> CgSpringUnitTestClassConstructor(context) SpringTestType.INTEGRATION_TEST -> - when (val settings = springCodeGenerationContext.springSettings) { - is PresentSpringSettings -> CgSpringIntegrationTestClassConstructor(context, springCodeGenerationContext, settings) + when (val settings = springSettings) { + is PresentSpringSettings -> CgSpringIntegrationTestClassConstructor(context, concreteContextLoadingResult, settings) is AbsentSpringSettings -> error("No Spring settings were provided for Spring integration test generation.") } } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/services/language/CgLanguageAssistant.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/services/language/CgLanguageAssistant.kt index db1f942dcc..9efa0e1996 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/services/language/CgLanguageAssistant.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/services/language/CgLanguageAssistant.kt @@ -1,6 +1,5 @@ package org.utbot.framework.codegen.services.language -import org.utbot.framework.codegen.domain.ProjectType import org.utbot.framework.codegen.domain.context.TestClassContext import org.utbot.framework.codegen.domain.context.CgContext import org.utbot.framework.codegen.renderer.CgPrinter @@ -16,11 +15,10 @@ import org.utbot.framework.codegen.tree.CgMethodConstructor import org.utbot.framework.codegen.tree.CgStatementConstructor import org.utbot.framework.codegen.tree.CgStatementConstructorImpl import org.utbot.framework.codegen.tree.CgVariableConstructor -import org.utbot.framework.codegen.tree.CgSpringVariableConstructor import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.CodegenLanguage -abstract class CgLanguageAssistant { +interface CgLanguageAssistant { companion object { fun getByCodegenLanguage(language: CodegenLanguage) = when (language) { @@ -30,31 +28,41 @@ abstract class CgLanguageAssistant { } } - open val outerMostTestClassContent: TestClassContext? = null + val outerMostTestClassContent: TestClassContext? - abstract val extension: String + val extension: String - abstract val languageKeywords: Set + val languageKeywords: Set - abstract fun testClassName( + fun testClassName( testClassCustomName: String?, testClassPackageName: String, classUnderTest: ClassId ): Pair - open fun getNameGeneratorBy(context: CgContext): CgNameGenerator = CgNameGeneratorImpl(context) - open fun getCallableAccessManagerBy(context: CgContext): CgCallableAccessManager = - CgCallableAccessManagerImpl(context) - open fun getStatementConstructorBy(context: CgContext): CgStatementConstructor = CgStatementConstructorImpl(context) + fun getNameGeneratorBy(context: CgContext): CgNameGenerator + fun getCallableAccessManagerBy(context: CgContext): CgCallableAccessManager + fun getStatementConstructorBy(context: CgContext): CgStatementConstructor - open fun getVariableConstructorBy(context: CgContext): CgVariableConstructor = when (context.projectType) { - ProjectType.Spring -> CgSpringVariableConstructor(context) - else -> CgVariableConstructor(context) - } + fun getVariableConstructorBy(context: CgContext): CgVariableConstructor + + fun getMethodConstructorBy(context: CgContext): CgMethodConstructor + fun getCgFieldStateManager(context: CgContext): CgFieldStateManager + + fun getLanguageTestFrameworkManager(): LanguageTestFrameworkManager + fun cgRenderer(context: CgRendererContext, printer: CgPrinter): CgAbstractRenderer +} + +abstract class AbstractCgLanguageAssistant : CgLanguageAssistant { + override val outerMostTestClassContent: TestClassContext? get() = null + + override fun getNameGeneratorBy(context: CgContext): CgNameGenerator = CgNameGeneratorImpl(context) + override fun getCallableAccessManagerBy(context: CgContext): CgCallableAccessManager = + CgCallableAccessManagerImpl(context) + override fun getStatementConstructorBy(context: CgContext): CgStatementConstructor = CgStatementConstructorImpl(context) - open fun getMethodConstructorBy(context: CgContext): CgMethodConstructor = CgMethodConstructor(context) - open fun getCgFieldStateManager(context: CgContext): CgFieldStateManager = CgFieldStateManagerImpl(context) + override fun getVariableConstructorBy(context: CgContext): CgVariableConstructor = CgVariableConstructor(context) - abstract fun getLanguageTestFrameworkManager(): LanguageTestFrameworkManager - abstract fun cgRenderer(context: CgRendererContext, printer: CgPrinter): CgAbstractRenderer + override fun getMethodConstructorBy(context: CgContext): CgMethodConstructor = CgMethodConstructor(context) + override fun getCgFieldStateManager(context: CgContext): CgFieldStateManager = CgFieldStateManagerImpl(context) } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/services/language/JavaCgLanguageAssistant.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/services/language/JavaCgLanguageAssistant.kt index db236ed252..4d084339cb 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/services/language/JavaCgLanguageAssistant.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/services/language/JavaCgLanguageAssistant.kt @@ -8,7 +8,7 @@ import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.JVMTestFrameworkManager import org.utbot.framework.plugin.api.utils.testClassNameGenerator -object JavaCgLanguageAssistant : CgLanguageAssistant() { +object JavaCgLanguageAssistant : AbstractCgLanguageAssistant() { override val extension: String get() = ".java" diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/services/language/KotlinCgLanguageAssistant.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/services/language/KotlinCgLanguageAssistant.kt index 1e5e40e474..2a37bfd5bf 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/services/language/KotlinCgLanguageAssistant.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/services/language/KotlinCgLanguageAssistant.kt @@ -8,7 +8,7 @@ import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.JVMTestFrameworkManager import org.utbot.framework.plugin.api.utils.testClassNameGenerator -object KotlinCgLanguageAssistant : CgLanguageAssistant() { +object KotlinCgLanguageAssistant : AbstractCgLanguageAssistant() { override val extension: String get() = ".kt" 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 34b6b750bd..4935c85ee4 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 @@ -13,13 +13,12 @@ import org.utbot.framework.codegen.domain.models.CgTestMethodType.SUCCESSFUL import org.utbot.framework.codegen.util.escapeControlChars import org.utbot.framework.codegen.util.resolve import org.utbot.framework.plugin.api.ClassId -import org.utbot.framework.plugin.api.SpringCodeGenerationContext +import org.utbot.framework.plugin.api.ConcreteContextLoadingResult import org.utbot.framework.plugin.api.SpringSettings.* import org.utbot.framework.plugin.api.SpringConfiguration.* import org.utbot.framework.plugin.api.util.IndentUtil.TAB import org.utbot.framework.plugin.api.util.SpringModelUtils.activeProfilesClassId import org.utbot.framework.plugin.api.util.SpringModelUtils.autoConfigureTestDbClassId -import org.utbot.framework.plugin.api.util.SpringModelUtils.autowiredClassId import org.utbot.framework.plugin.api.util.SpringModelUtils.bootstrapWithClassId import org.utbot.framework.plugin.api.util.SpringModelUtils.contextConfigurationClassId import org.utbot.framework.plugin.api.util.SpringModelUtils.crudRepositoryClassId @@ -37,7 +36,7 @@ import org.utbot.spring.api.UTSpringContextLoadingException class CgSpringIntegrationTestClassConstructor( context: CgContext, - private val springCodeGenerationContext: SpringCodeGenerationContext, + private val concreteContextLoadingResult: ConcreteContextLoadingResult?, private val springSettings: PresentSpringSettings, ) : CgAbstractSpringTestClassConstructor(context) { @@ -64,21 +63,20 @@ class CgSpringIntegrationTestClassConstructor( ) private fun constructContextLoadsMethod() : CgTestMethod { - val contextLoadingResult = springCodeGenerationContext.springContextLoadingResult - if (contextLoadingResult == null) + if (concreteContextLoadingResult == null) logger.error { "Missing contextLoadingResult" } - val exception = contextLoadingResult?.exceptions?.firstOrNull() + val exception = concreteContextLoadingResult?.exceptions?.firstOrNull() return CgTestMethod( name = "contextLoads", statements = listOfNotNull( exception?.let { e -> constructFailedContextLoadingTraceComment(e) }, - if (contextLoadingResult == null) CgSingleLineComment("Error: context loading result from concrete execution is missing") else null + if (concreteContextLoadingResult == null) CgSingleLineComment("Error: context loading result from concrete execution is missing") else null ), annotations = listOf(addAnnotation(context.testFramework.testAnnotationId, Method)), documentation = CgDocumentationComment(listOf( CgDocRegularLineStmt("This sanity check test fails if the application context cannot start.") ) + exception?.let { constructFailedContextLoadingDocComment() }.orEmpty()), - type = if (contextLoadingResult != null && exception == null) SUCCESSFUL else FAILING + type = if (concreteContextLoadingResult != null && exception == null) SUCCESSFUL else FAILING ) } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/context/ApplicationContext.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/context/ApplicationContext.kt new file mode 100644 index 0000000000..8a17675ee5 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/ApplicationContext.kt @@ -0,0 +1,17 @@ +package org.utbot.framework.context + +import org.utbot.framework.codegen.generator.AbstractCodeGenerator +import org.utbot.framework.codegen.generator.CodeGeneratorParams + +interface ApplicationContext { + val mockerContext: MockerContext + val typeReplacer: TypeReplacer + val nonNullSpeculator: NonNullSpeculator + + fun createConcreteExecutionContext( + fullClasspath: String, + classpathWithoutDependencies: String + ): ConcreteExecutionContext + + fun createCodeGenerator(params: CodeGeneratorParams): AbstractCodeGenerator +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/context/ConcreteExecutionContext.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/context/ConcreteExecutionContext.kt new file mode 100644 index 0000000000..c9b8103345 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/ConcreteExecutionContext.kt @@ -0,0 +1,29 @@ +package org.utbot.framework.context + +import org.utbot.framework.plugin.api.ClassId +import org.utbot.framework.plugin.api.ConcreteContextLoadingResult +import org.utbot.framework.plugin.api.UtExecution +import org.utbot.fuzzer.IdentityPreservingIdGenerator +import org.utbot.fuzzing.JavaValueProvider +import org.utbot.instrumentation.ConcreteExecutor +import org.utbot.instrumentation.instrumentation.execution.UtConcreteExecutionResult +import org.utbot.instrumentation.instrumentation.execution.UtExecutionInstrumentation + +interface ConcreteExecutionContext { + val instrumentationFactory: UtExecutionInstrumentation.Factory<*> + + fun loadContext( + concreteExecutor: ConcreteExecutor, + ): ConcreteContextLoadingResult + + fun transformExecutionsBeforeMinimization( + executions: List, + classUnderTestId: ClassId + ): List + + fun tryCreateValueProvider( + concreteExecutor: ConcreteExecutor, + classUnderTest: ClassId, + idGenerator: IdentityPreservingIdGenerator, + ): JavaValueProvider +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/context/MockerContext.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/context/MockerContext.kt new file mode 100644 index 0000000000..a85f591f5d --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/MockerContext.kt @@ -0,0 +1,13 @@ +package org.utbot.framework.context + +interface MockerContext { + /** + * Shows if we have installed framework dependencies + */ + val mockFrameworkInstalled: Boolean + + /** + * Shows if we have installed static mocking tools + */ + val staticsMockingIsConfigured: Boolean +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/context/NonNullSpeculator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/context/NonNullSpeculator.kt new file mode 100644 index 0000000000..3c4830cd45 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/NonNullSpeculator.kt @@ -0,0 +1,17 @@ +package org.utbot.framework.context + +import org.utbot.framework.plugin.api.ClassId +import soot.SootField + +interface NonNullSpeculator { + /** + * Checks whether accessing [field] (with a method invocation or field access) speculatively + * cannot produce [NullPointerException] (according to its finality or accessibility). + * + * @see docs/SpeculativeFieldNonNullability.md for more information. + */ + fun speculativelyCannotProduceNullPointerException( + field: SootField, + classUnderTest: ClassId, + ): Boolean +} diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/context/TypeReplacer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/context/TypeReplacer.kt new file mode 100644 index 0000000000..52c3ee8604 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/TypeReplacer.kt @@ -0,0 +1,18 @@ +package org.utbot.framework.context + +import org.utbot.framework.plugin.api.ClassId +import org.utbot.framework.plugin.api.TypeReplacementMode +import soot.RefType + +interface TypeReplacer { + /** + * Shows if there are any restrictions on type implementors. + */ + val typeReplacementMode: TypeReplacementMode + + /** + * Finds a type to replace the original abstract type + * if it is guided with some additional information. + */ + fun replaceTypeIfNeeded(type: RefType): ClassId? +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/context/custom/CoverageFilteringConcreteExecutionContext.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/context/custom/CoverageFilteringConcreteExecutionContext.kt new file mode 100644 index 0000000000..b7ff805157 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/custom/CoverageFilteringConcreteExecutionContext.kt @@ -0,0 +1,93 @@ +package org.utbot.framework.context.custom + +import mu.KotlinLogging +import org.utbot.common.hasOnClasspath +import org.utbot.common.tryLoadClass +import org.utbot.framework.context.ConcreteExecutionContext +import org.utbot.framework.plugin.api.ClassId +import org.utbot.framework.plugin.api.UtExecution +import org.utbot.framework.plugin.api.util.utContext +import java.io.File +import java.net.URLClassLoader + +/** + * Decorator of [delegateContext] that filters coverage before test set minimization + * (see [transformExecutionsBeforeMinimization]) to avoid generating too many tests that + * only increase coverage of third party libraries. + * + * This implementation: + * - always keeps instructions that are in class under test (even if other rules say otherwise) + * - filters out instructions in classes marked with annotations from [annotationsToIgnoreCoverage] + * - filters out instructions from classes that are not found in `classpathToIncludeCoverageFrom` + * + * Finally, if [keepOriginalCoverageOnEmptyFilteredCoverage] is `true` we restore original coverage + * for executions whose coverage becomes empty after filtering. + */ +class CoverageFilteringConcreteExecutionContext( + private val delegateContext: ConcreteExecutionContext, + classpathToIncludeCoverageFrom: String, + private val annotationsToIgnoreCoverage: Set, + private val keepOriginalCoverageOnEmptyFilteredCoverage: Boolean, +) : ConcreteExecutionContext by delegateContext { + private val urlsToIncludeCoverageFrom = classpathToIncludeCoverageFrom.split(File.pathSeparator) + .map { File(it).toURI().toURL() } + .toTypedArray() + + companion object { + private val logger = KotlinLogging.logger {} + } + + override fun transformExecutionsBeforeMinimization( + executions: List, + classUnderTestId: ClassId + ): List { + val annotationsToIgnoreCoverage = + annotationsToIgnoreCoverage.mapNotNull { utContext.classLoader.tryLoadClass(it.name) } + + val classLoaderToIncludeCoverageFrom = URLClassLoader(urlsToIncludeCoverageFrom, null) + + val classesToIncludeCoverageFromCache = mutableMapOf() + + return executions.map { execution -> + val coverage = execution.coverage ?: return@map execution + + val filteredCoveredInstructions = + coverage.coveredInstructions + .filter { instruction -> + val instrClassName = instruction.className + + classesToIncludeCoverageFromCache.getOrPut(instrClassName) { + instrClassName == classUnderTestId.name || + (classLoaderToIncludeCoverageFrom.hasOnClasspath(instrClassName) && + !hasAnnotations(instrClassName, annotationsToIgnoreCoverage)) + } + } + .ifEmpty { + if (keepOriginalCoverageOnEmptyFilteredCoverage) { + logger.warn("Execution covered instruction list became empty. Proceeding with not filtered instruction list.") + coverage.coveredInstructions + } else { + logger.warn("Execution covered instruction list became empty. Proceeding with empty coverage.") + emptyList() + } + } + + execution.copy( + coverage = coverage.copy( + coveredInstructions = filteredCoveredInstructions + ) + ) + } + } + + private fun hasAnnotations(className: String, annotations: List>): Boolean = + utContext + .classLoader + .loadClass(className) + .annotations + .any { existingAnnotation -> + annotations.any { annotation -> + annotation.isInstance(existingAnnotation) + } + } +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/context/simple/SimpleApplicationContext.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/context/simple/SimpleApplicationContext.kt new file mode 100644 index 0000000000..e3f18b5cb7 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/simple/SimpleApplicationContext.kt @@ -0,0 +1,27 @@ +package org.utbot.framework.context.simple + +import org.utbot.framework.codegen.generator.AbstractCodeGenerator +import org.utbot.framework.codegen.generator.CodeGenerator +import org.utbot.framework.codegen.generator.CodeGeneratorParams +import org.utbot.framework.context.ApplicationContext +import org.utbot.framework.context.ConcreteExecutionContext +import org.utbot.framework.context.MockerContext +import org.utbot.framework.context.NonNullSpeculator +import org.utbot.framework.context.TypeReplacer + +/** + * A context to use when no specific data is required. + */ +class SimpleApplicationContext( + override val mockerContext: MockerContext, + override val typeReplacer: TypeReplacer = SimpleTypeReplacer(), + override val nonNullSpeculator: NonNullSpeculator = SimpleNonNullSpeculator() +) : ApplicationContext { + override fun createConcreteExecutionContext( + fullClasspath: String, + classpathWithoutDependencies: String + ): ConcreteExecutionContext = SimpleConcreteExecutionContext(fullClasspath) + + override fun createCodeGenerator(params: CodeGeneratorParams): AbstractCodeGenerator = + CodeGenerator(params) +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/context/simple/SimpleConcreteExecutionContext.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/context/simple/SimpleConcreteExecutionContext.kt new file mode 100644 index 0000000000..bdbd67c0c2 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/simple/SimpleConcreteExecutionContext.kt @@ -0,0 +1,35 @@ +package org.utbot.framework.context.simple + +import org.utbot.framework.context.ConcreteExecutionContext +import org.utbot.framework.plugin.api.ClassId +import org.utbot.framework.plugin.api.ConcreteContextLoadingResult +import org.utbot.framework.plugin.api.UtExecution +import org.utbot.fuzzer.IdentityPreservingIdGenerator +import org.utbot.fuzzing.JavaValueProvider +import org.utbot.fuzzing.ValueProvider +import org.utbot.fuzzing.defaultValueProviders +import org.utbot.instrumentation.ConcreteExecutor +import org.utbot.instrumentation.instrumentation.execution.SimpleUtExecutionInstrumentation +import org.utbot.instrumentation.instrumentation.execution.UtConcreteExecutionResult +import org.utbot.instrumentation.instrumentation.execution.UtExecutionInstrumentation +import java.io.File + +class SimpleConcreteExecutionContext(fullClassPath: String) : ConcreteExecutionContext { + override val instrumentationFactory: UtExecutionInstrumentation.Factory<*> = + SimpleUtExecutionInstrumentation.Factory(fullClassPath.split(File.pathSeparator).toSet()) + + override fun loadContext( + concreteExecutor: ConcreteExecutor, + ): ConcreteContextLoadingResult = ConcreteContextLoadingResult.successWithoutExceptions() + + override fun transformExecutionsBeforeMinimization( + executions: List, + classUnderTestId: ClassId + ): List = executions + + override fun tryCreateValueProvider( + concreteExecutor: ConcreteExecutor, + classUnderTest: ClassId, + idGenerator: IdentityPreservingIdGenerator + ): JavaValueProvider = ValueProvider.of(defaultValueProviders(idGenerator)) +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/context/simple/SimpleMockerContext.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/context/simple/SimpleMockerContext.kt new file mode 100644 index 0000000000..e7b0977d9e --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/simple/SimpleMockerContext.kt @@ -0,0 +1,16 @@ +package org.utbot.framework.context.simple + +import org.utbot.framework.context.MockerContext + +class SimpleMockerContext( + override val mockFrameworkInstalled: Boolean, + staticsMockingIsConfigured: Boolean +) : MockerContext { + /** + * NOTE: Can only be `true` when [mockFrameworkInstalled], because + * situation when mock framework is not installed but static mocking + * is configured is semantically incorrect. + */ + override val staticsMockingIsConfigured: Boolean = + mockFrameworkInstalled && staticsMockingIsConfigured +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/context/simple/SimpleNonNullSpeculator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/context/simple/SimpleNonNullSpeculator.kt new file mode 100644 index 0000000000..b1b8404190 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/simple/SimpleNonNullSpeculator.kt @@ -0,0 +1,17 @@ +package org.utbot.framework.context.simple + +import org.utbot.framework.UtSettings +import org.utbot.framework.context.NonNullSpeculator +import org.utbot.framework.isFromTrustedLibrary +import org.utbot.framework.plugin.api.ClassId +import soot.SootField + +class SimpleNonNullSpeculator : NonNullSpeculator { + override fun speculativelyCannotProduceNullPointerException( + field: SootField, + classUnderTest: ClassId, + ): Boolean = + !UtSettings.maximizeCoverageUsingReflection && + field.declaringClass.isFromTrustedLibrary() && + (field.isFinal || !field.isPublic) +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/context/simple/SimpleTypeReplacer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/context/simple/SimpleTypeReplacer.kt new file mode 100644 index 0000000000..300aea86e5 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/simple/SimpleTypeReplacer.kt @@ -0,0 +1,12 @@ +package org.utbot.framework.context.simple + +import org.utbot.framework.context.TypeReplacer +import org.utbot.framework.plugin.api.ClassId +import org.utbot.framework.plugin.api.TypeReplacementMode +import soot.RefType + +class SimpleTypeReplacer : TypeReplacer { + override val typeReplacementMode: TypeReplacementMode = TypeReplacementMode.AnyImplementor + + override fun replaceTypeIfNeeded(type: RefType): ClassId? = null +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringApplicationContext.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringApplicationContext.kt new file mode 100644 index 0000000000..458c9db3f6 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringApplicationContext.kt @@ -0,0 +1,25 @@ +package org.utbot.framework.context.spring + +import org.utbot.framework.context.ApplicationContext +import org.utbot.framework.plugin.api.BeanDefinitionData +import org.utbot.framework.plugin.api.ClassId +import org.utbot.framework.plugin.api.ConcreteContextLoadingResult +import org.utbot.framework.plugin.api.SpringSettings + +/** + * Data we get from Spring application context + * to manage engine and code generator behaviour. + */ +interface SpringApplicationContext : ApplicationContext { + val springSettings: SpringSettings + + /** + * Describes bean definitions (bean name, type, some optional additional data) + */ + val beanDefinitions: List + val injectedTypes: Set + val allInjectedSuperTypes: Set + + var concreteContextLoadingResult: ConcreteContextLoadingResult? + fun getBeansAssignableTo(classId: ClassId): List +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringApplicationContextImpl.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringApplicationContextImpl.kt new file mode 100644 index 0000000000..4e9bf11553 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringApplicationContextImpl.kt @@ -0,0 +1,135 @@ +package org.utbot.framework.context.spring + +import mu.KotlinLogging +import org.utbot.common.isAbstract +import org.utbot.common.isStatic +import org.utbot.framework.codegen.generator.AbstractCodeGenerator +import org.utbot.framework.codegen.generator.CodeGeneratorParams +import org.utbot.framework.codegen.generator.SpringCodeGenerator +import org.utbot.framework.context.ApplicationContext +import org.utbot.framework.context.ConcreteExecutionContext +import org.utbot.framework.context.NonNullSpeculator +import org.utbot.framework.context.TypeReplacer +import org.utbot.framework.context.custom.CoverageFilteringConcreteExecutionContext +import org.utbot.framework.plugin.api.BeanDefinitionData +import org.utbot.framework.plugin.api.ClassId +import org.utbot.framework.plugin.api.ConcreteContextLoadingResult +import org.utbot.framework.plugin.api.SpringSettings +import org.utbot.framework.plugin.api.SpringTestType +import org.utbot.framework.plugin.api.util.SpringModelUtils.entityClassIds +import org.utbot.framework.plugin.api.util.allSuperTypes +import org.utbot.framework.plugin.api.util.id +import org.utbot.framework.plugin.api.util.jClass +import org.utbot.framework.plugin.api.util.utContext + +class SpringApplicationContextImpl( + private val delegateContext: ApplicationContext, + override val beanDefinitions: List = emptyList(), + private val springTestType: SpringTestType, + override val springSettings: SpringSettings, +): ApplicationContext by delegateContext, SpringApplicationContext { + companion object { + private val logger = KotlinLogging.logger {} + } + + override val typeReplacer: TypeReplacer = SpringTypeReplacer(delegateContext.typeReplacer, this) + override val nonNullSpeculator: NonNullSpeculator = SpringNonNullSpeculator(delegateContext.nonNullSpeculator, this) + + override var concreteContextLoadingResult: ConcreteContextLoadingResult? = null + + override fun createConcreteExecutionContext( + fullClasspath: String, + classpathWithoutDependencies: String + ): ConcreteExecutionContext { + var delegateConcreteExecutionContext = delegateContext.createConcreteExecutionContext( + fullClasspath, + classpathWithoutDependencies + ) + + // to avoid filtering out all coverage, we only filter + // coverage when `classpathWithoutDependencies` is provided + // (e.g. when we are launched from IDE plugin) + if (classpathWithoutDependencies.isNotEmpty()) + delegateConcreteExecutionContext = CoverageFilteringConcreteExecutionContext( + delegateContext = delegateConcreteExecutionContext, + classpathToIncludeCoverageFrom = classpathWithoutDependencies, + annotationsToIgnoreCoverage = entityClassIds.toSet(), + keepOriginalCoverageOnEmptyFilteredCoverage = true + ) + + return when (springTestType) { + SpringTestType.UNIT_TEST -> delegateConcreteExecutionContext + SpringTestType.INTEGRATION_TEST -> SpringIntegrationTestConcreteExecutionContext( + delegateConcreteExecutionContext, + classpathWithoutDependencies, + this + ) + } + } + + override fun createCodeGenerator(params: CodeGeneratorParams): AbstractCodeGenerator = + // TODO decorate original `delegateContext.createCodeGenerator(params)` + SpringCodeGenerator( + springTestType = springTestType, + springSettings = springSettings, + concreteContextLoadingResult = concreteContextLoadingResult, + params = params, + ) + + override fun getBeansAssignableTo(classId: ClassId): List = beanDefinitions.filter { beanDef -> + // some bean classes may fail to load + runCatching { + val beanClass = ClassId(beanDef.beanTypeName).jClass + classId.jClass.isAssignableFrom(beanClass) + }.getOrElse { false } + } + + // Classes representing concrete types that are actually used in Spring application + override val injectedTypes: Set + get() { + if (!areAllInjectedSuperTypesInitialized) { + for (beanTypeName in beanDefinitions.map { it.beanTypeName }) { + try { + val beanClass = utContext.classLoader.loadClass(beanTypeName) + if (!beanClass.isAbstract && !beanClass.isInterface && + !beanClass.isLocalClass && (!beanClass.isMemberClass || beanClass.isStatic)) { + _injectedTypes += beanClass.id + } + } catch (e: Throwable) { + // For some Spring beans (e.g. with anonymous classes) + // it is possible to have problems with classes loading. + when (e) { + is ClassNotFoundException, is NoClassDefFoundError, is IllegalAccessError -> + logger.warn { "Failed to load bean class for $beanTypeName (${e.message})" } + + else -> throw e + } + } + } + + // This is done to be sure that this storage is not empty after the first class loading iteration. + // So, even if all loaded classes were filtered out, we will not try to load them again. + areAllInjectedSuperTypesInitialized = true + } + + return _injectedTypes + } + + override val allInjectedSuperTypes: Set + get() { + if (!areInjectedTypesInitialized) { + _allInjectedSuperTypes = injectedTypes.flatMap { it.allSuperTypes() }.toSet() + areInjectedTypesInitialized = true + } + + return _allInjectedSuperTypes + } + + // the following properties help to imitate `by lazy` behaviour, do not use them directly + // (we can't use actual `by lazy` because communication via RD breaks it) + private var _allInjectedSuperTypes: Set = emptySet() + private var areAllInjectedSuperTypesInitialized : Boolean = false + + private val _injectedTypes = mutableSetOf() + private var areInjectedTypesInitialized: Boolean = false +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringIntegrationTestConcreteExecutionContext.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringIntegrationTestConcreteExecutionContext.kt new file mode 100644 index 0000000000..87b7387e03 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringIntegrationTestConcreteExecutionContext.kt @@ -0,0 +1,119 @@ +package org.utbot.framework.context.spring + +import mu.KotlinLogging +import org.utbot.common.tryLoadClass +import org.utbot.framework.context.ConcreteExecutionContext +import org.utbot.framework.plugin.api.ClassId +import org.utbot.framework.plugin.api.ConcreteContextLoadingResult +import org.utbot.framework.plugin.api.SpringRepositoryId +import org.utbot.framework.plugin.api.SpringSettings +import org.utbot.framework.plugin.api.UtExecution +import org.utbot.framework.plugin.api.util.SpringModelUtils +import org.utbot.framework.plugin.api.util.allDeclaredFieldIds +import org.utbot.framework.plugin.api.util.jField +import org.utbot.framework.plugin.api.util.utContext +import org.utbot.fuzzer.IdentityPreservingIdGenerator +import org.utbot.fuzzing.JavaValueProvider +import org.utbot.fuzzing.ValueProvider +import org.utbot.fuzzing.providers.FieldValueProvider +import org.utbot.fuzzing.providers.ObjectValueProvider +import org.utbot.fuzzing.spring.SavedEntityValueProvider +import org.utbot.fuzzing.spring.SpringBeanValueProvider +import org.utbot.instrumentation.ConcreteExecutor +import org.utbot.instrumentation.getRelevantSpringRepositories +import org.utbot.instrumentation.instrumentation.execution.UtConcreteExecutionResult +import org.utbot.instrumentation.instrumentation.execution.UtExecutionInstrumentation +import org.utbot.instrumentation.instrumentation.spring.SpringUtExecutionInstrumentation +import org.utbot.instrumentation.tryLoadingSpringContext +import java.io.File + +class SpringIntegrationTestConcreteExecutionContext( + private val delegateContext: ConcreteExecutionContext, + classpathWithoutDependencies: String, + private val springApplicationContext: SpringApplicationContext, +) : ConcreteExecutionContext { + private val springSettings = (springApplicationContext.springSettings as? SpringSettings.PresentSpringSettings) ?: + error("Integration tests cannot be generated without Spring configuration") + + companion object { + private val logger = KotlinLogging.logger {} + } + + override val instrumentationFactory: UtExecutionInstrumentation.Factory<*> = + SpringUtExecutionInstrumentation.Factory( + delegateContext.instrumentationFactory, + springSettings, + springApplicationContext.beanDefinitions, + buildDirs = classpathWithoutDependencies.split(File.pathSeparator) + .map { File(it).toURI().toURL() } + .toTypedArray(), + ) + + override fun loadContext( + concreteExecutor: ConcreteExecutor, + ): ConcreteContextLoadingResult = + delegateContext.loadContext(concreteExecutor).andThen { + springApplicationContext.concreteContextLoadingResult ?: concreteExecutor.tryLoadingSpringContext().also { + springApplicationContext.concreteContextLoadingResult = it + } + } + + override fun transformExecutionsBeforeMinimization( + executions: List, + classUnderTestId: ClassId + ): List = delegateContext.transformExecutionsBeforeMinimization(executions, classUnderTestId) + + override fun tryCreateValueProvider( + concreteExecutor: ConcreteExecutor, + classUnderTest: ClassId, + idGenerator: IdentityPreservingIdGenerator + ): JavaValueProvider { + if (springApplicationContext.getBeansAssignableTo(classUnderTest).isEmpty()) + error( + "No beans of type ${classUnderTest.name} are found. " + + "Try choosing different Spring configuration or adding beans to " + + springSettings.configuration.fullDisplayName + ) + + val relevantRepositories = concreteExecutor.getRelevantSpringRepositories(classUnderTest) + logger.info { "Detected relevant repositories for class $classUnderTest: $relevantRepositories" } + + // spring should try to generate bean values, but if it fails, then object value provider is used for it + val springBeanValueProvider = SpringBeanValueProvider( + idGenerator, + beanNameProvider = { classId -> + springApplicationContext.getBeansAssignableTo(classId).map { it.beanName } + }, + relevantRepositories = relevantRepositories + ).withFallback(ObjectValueProvider(idGenerator)) + + return delegateContext.tryCreateValueProvider(concreteExecutor, classUnderTest, idGenerator) + .except { p -> p is ObjectValueProvider } + .with(springBeanValueProvider) + .with(createSavedEntityValueProviders(relevantRepositories, idGenerator)) + .with(createFieldValueProviders(relevantRepositories, idGenerator)) + } + + private fun createSavedEntityValueProviders( + relevantRepositories: Set, + idGenerator: IdentityPreservingIdGenerator + ) = ValueProvider.of(relevantRepositories.map { SavedEntityValueProvider(idGenerator, it) }) + + private fun createFieldValueProviders( + relevantRepositories: Set, + idGenerator: IdentityPreservingIdGenerator + ): JavaValueProvider { + val generatedValueAnnotationClasses = SpringModelUtils.generatedValueClassIds.mapNotNull { + @Suppress("UNCHECKED_CAST") // type system fails to understand that @GeneratedValue is indeed an annotation + utContext.classLoader.tryLoadClass(it.name) as Class? + } + + val generatedValueFieldIds = + relevantRepositories + .flatMap { it.entityClassId.allDeclaredFieldIds } + .filter { fieldId -> generatedValueAnnotationClasses.any { fieldId.jField.isAnnotationPresent(it) } } + logger.info { "Detected @GeneratedValue fields: $generatedValueFieldIds" } + + return ValueProvider.of(generatedValueFieldIds.map { FieldValueProvider(idGenerator, it) }) + } +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringNonNullSpeculator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringNonNullSpeculator.kt new file mode 100644 index 0000000000..5f49d7800d --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringNonNullSpeculator.kt @@ -0,0 +1,19 @@ +package org.utbot.framework.context.spring + +import org.utbot.framework.context.NonNullSpeculator +import org.utbot.framework.plugin.api.ClassId +import org.utbot.framework.plugin.api.classId +import org.utbot.framework.plugin.api.util.allDeclaredFieldIds +import org.utbot.framework.plugin.api.util.fieldId +import soot.SootField + +class SpringNonNullSpeculator( + private val delegateNonNullSpeculator: NonNullSpeculator, + private val springApplicationContext: SpringApplicationContext +) : NonNullSpeculator { + override fun speculativelyCannotProduceNullPointerException(field: SootField, classUnderTest: ClassId): Boolean = + // TODO add ` || delegateNonNullSpeculator.speculativelyCannotProduceNullPointerException(field, classUnderTest)` + // (TODO is added as a part of only equivalent transformations refactoring PR and should be completed in the follow up PR) + field.fieldId in classUnderTest.allDeclaredFieldIds && field.type.classId !in springApplicationContext.allInjectedSuperTypes + +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringTypeReplacer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringTypeReplacer.kt new file mode 100644 index 0000000000..78362c7520 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringTypeReplacer.kt @@ -0,0 +1,26 @@ +package org.utbot.framework.context.spring + +import org.utbot.framework.context.TypeReplacer +import org.utbot.framework.plugin.api.ClassId +import org.utbot.framework.plugin.api.TypeReplacementMode +import org.utbot.framework.plugin.api.id +import org.utbot.framework.plugin.api.isAbstractType +import org.utbot.framework.plugin.api.util.isSubtypeOf +import soot.RefType + +class SpringTypeReplacer( + private val delegateTypeReplacer: TypeReplacer, + private val springApplicationContext: SpringApplicationContext +) : TypeReplacer { + override val typeReplacementMode: TypeReplacementMode = + // TODO add ` || delegateTypeReplacer.typeReplacementMode == TypeReplacementMode.KnownImplementor` + // (TODO is added as a part of only equivalent transformations refactoring PR and should be completed in the follow up PR) + if (springApplicationContext.beanDefinitions.isNotEmpty()) TypeReplacementMode.KnownImplementor + else TypeReplacementMode.NoImplementors + + override fun replaceTypeIfNeeded(type: RefType): ClassId? = + // TODO add `delegateTypeReplacer.replaceTypeIfNeeded(type) ?: ` + // (TODO is added as a part of only equivalent transformations refactoring PR and should be completed in the follow up PR) + if (type.isAbstractType) springApplicationContext.injectedTypes.singleOrNull { it.isSubtypeOf(type.id) } + else null +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/coverage/CoverageCalculator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/coverage/CoverageCalculator.kt index cfefe3c12e..c662da9b31 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/coverage/CoverageCalculator.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/coverage/CoverageCalculator.kt @@ -13,7 +13,7 @@ import kotlinx.coroutines.runBlocking fun methodCoverage(executable: ExecutableId, executions: List>, classpath: String): Coverage { val methodSignature = executable.signature val classId = executable.classId - return ConcreteExecutor(CoverageInstrumentation, classpath).let { executor -> + return ConcreteExecutor(CoverageInstrumentation.Factory, classpath).let { executor -> for (execution in executions) { val args = execution.stateBefore.params.map { it.value }.toMutableList() val caller = execution.stateBefore.caller diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestCaseGenerator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestCaseGenerator.kt index ba78166b30..7980c7e3be 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestCaseGenerator.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestCaseGenerator.kt @@ -7,13 +7,13 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.emitAll import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import kotlinx.coroutines.yield import mu.KLogger import mu.KotlinLogging import org.utbot.common.* -import org.utbot.common.PathUtil.toURL import org.utbot.engine.EngineController import org.utbot.engine.Mocker import org.utbot.engine.UtBotSymbolicEngine @@ -25,9 +25,11 @@ import org.utbot.framework.UtSettings.checkSolverTimeoutMillis import org.utbot.framework.UtSettings.disableCoroutinesDebug import org.utbot.framework.UtSettings.utBotGenerationTimeoutInMillis import org.utbot.framework.UtSettings.warmupConcreteExecution +import org.utbot.framework.context.ApplicationContext +import org.utbot.framework.context.simple.SimpleApplicationContext +import org.utbot.framework.context.simple.SimpleMockerContext import org.utbot.framework.plugin.api.utils.checkFrameworkDependencies import org.utbot.framework.minimization.minimizeTestCase -import org.utbot.framework.plugin.api.util.SpringModelUtils.entityClassIds import org.utbot.framework.plugin.api.util.id import org.utbot.framework.plugin.api.util.utContext import org.utbot.framework.plugin.services.JdkInfo @@ -39,13 +41,9 @@ import org.utbot.framework.util.toModel import org.utbot.framework.plugin.api.SpringSettings.* import org.utbot.framework.plugin.api.SpringTestType.* import org.utbot.instrumentation.ConcreteExecutor -import org.utbot.instrumentation.instrumentation.spring.SpringUtExecutionInstrumentation -import org.utbot.instrumentation.instrumentation.execution.UtExecutionInstrumentation -import org.utbot.instrumentation.tryLoadingSpringContext import org.utbot.instrumentation.warmup import org.utbot.taint.TaintConfigurationProvider import java.io.File -import java.net.URLClassLoader import java.nio.file.Path import kotlin.coroutines.cancellation.CancellationException import kotlin.math.min @@ -68,28 +66,19 @@ open class TestCaseGenerator( val engineActions: MutableList<(UtBotSymbolicEngine) -> Unit> = mutableListOf(), val isCanceled: () -> Boolean = { false }, val forceSootReload: Boolean = true, - val applicationContext: ApplicationContext = ApplicationContext(), + val applicationContext: ApplicationContext = SimpleApplicationContext( + SimpleMockerContext( + mockFrameworkInstalled = true, + staticsMockingIsConfigured = true + ) + ), ) { private val logger: KLogger = KotlinLogging.logger {} private val timeoutLogger: KLogger = KotlinLogging.logger(logger.name + ".timeout") - private val executionInstrumentation by lazy { - when (applicationContext) { - is SpringApplicationContext -> when (val settings = applicationContext.springSettings) { - is AbsentSpringSettings -> UtExecutionInstrumentation - is PresentSpringSettings -> when (applicationContext.springTestType) { - UNIT_TEST -> UtExecutionInstrumentation - INTEGRATION_TEST -> SpringUtExecutionInstrumentation( - UtExecutionInstrumentation, - settings, - applicationContext.beanDefinitions, - buildDirs.map { it.toURL() }.toTypedArray(), - ) - } - } - - else -> UtExecutionInstrumentation - } - } + private val concreteExecutionContext = applicationContext.createConcreteExecutionContext( + fullClasspath = classpathForEngine, + classpathWithoutDependencies = buildDirs.joinToString(File.pathSeparator) + ) private val classpathForEngine: String get() = (buildDirs + listOfNotNull(classpath)).joinToString(File.pathSeparator) @@ -114,7 +103,7 @@ open class TestCaseGenerator( // force pool to create an appropriate executor // TODO ensure that instrumented process that starts here is properly terminated ConcreteExecutor( - executionInstrumentation, + concreteExecutionContext.instrumentationFactory, classpathForEngine, ).apply { warmup() @@ -128,12 +117,12 @@ open class TestCaseGenerator( TestSelectionStrategyType.DO_NOT_MINIMIZE_STRATEGY -> executions TestSelectionStrategyType.COVERAGE_STRATEGY -> minimizeTestCase( - when (applicationContext) { - is SpringApplicationContext -> executions.filterCoveredInstructions(classUnderTestId) - else -> executions - } + concreteExecutionContext.transformExecutionsBeforeMinimization( + executions, + classUnderTestId + ), + executionToTestSuite = { it.result::class.java } ) - { it.result::class.java } } @Throws(CancellationException::class) @@ -148,9 +137,9 @@ open class TestCaseGenerator( if (isCanceled()) return@flow - doContextDependentPreparationForTestGeneration() - applicationContext.getErrors().forEach { emit(it) } - if (applicationContext.preventsFurtherTestGeneration()) + val contextLoadingResult = loadConcreteExecutionContext() + emitAll(flowOf(*contextLoadingResult.utErrors.toTypedArray())) + if (!contextLoadingResult.contextLoaded) return@flow try { @@ -182,13 +171,13 @@ open class TestCaseGenerator( ): List = ConcreteExecutor.defaultPool.use { _ -> // TODO: think on appropriate way to close instrumented processes if (isCanceled()) return@use methods.map { UtMethodTestSet(it) } - doContextDependentPreparationForTestGeneration() + val contextLoadingResult = loadConcreteExecutionContext() val method2errors: Map> = methods.associateWith { - applicationContext.getErrors().associateTo(mutableMapOf()) { it.description to 1 } + contextLoadingResult.utErrors.associateTo(mutableMapOf()) { it.description to 1 } } - if (applicationContext.preventsFurtherTestGeneration()) + if (!contextLoadingResult.contextLoaded) return@use methods.map { method -> UtMethodTestSet(method, errors = method2errors.getValue(method)) } val executionStartInMillis = System.currentTimeMillis() @@ -321,7 +310,7 @@ open class TestCaseGenerator( mockStrategy = mockStrategyApi.toModel(), chosenClassesToMockAlways = chosenClassesToMockAlways, applicationContext = applicationContext, - executionInstrumentation = executionInstrumentation, + concreteExecutionContext = concreteExecutionContext, solverTimeoutInMillis = executionTimeEstimator.updatedSolverCheckTimeoutMillis, userTaintConfigurationProvider = userTaintConfigurationProvider, ) @@ -379,91 +368,9 @@ open class TestCaseGenerator( } } - private fun List.filterCoveredInstructions(classUnderTestId: ClassId): List { - // Do nothing when we were launched not from IDEA or when there are no executions - if (buildDirs.isEmpty() || this.isEmpty()) return this - - // List of annotations that we want to find in execution instructions - // in order to exclude such instructions for some reason - // E.g. we exclude instructions (which classes have @Entity) when it is not a class under test - // because we are not interested in coverage which was possibly produced by Spring itself - val annotationsToIgnoreCoverage = - entityClassIds.mapNotNull { utContext.classLoader.tryLoadClass(it.name) } - - val buildDirsClassLoader = createBuildDirsClassLoader() - val isClassOnUserClasspathCache = mutableMapOf() - - // Here we filter out instructions from third-party libraries - // Also, we filter out instructions that operate - // in classes marked with annotations from [annotationsToIgnore] - // and in standard java libs - return this.map { execution -> - val coverage = execution.coverage ?: return@map execution - - val filteredCoveredInstructions = - coverage.coveredInstructions - .filter { instruction -> - val instrClassName = instruction.className - - val isInstrClassOnClassPath = - isClassOnUserClasspathCache.getOrPut(instrClassName) { - buildDirsClassLoader.hasOnClasspath(instrClassName) - } - - // We want to - // - always keep instructions that are in class under test - // - ignore instructions in classes marked with annotations from [annotationsToIgnore] - return@filter instrClassName == classUnderTestId.name || - (isInstrClassOnClassPath && !hasAnnotations(instrClassName, annotationsToIgnoreCoverage)) - } - .ifEmpty { - coverage.coveredInstructions - .also { - logger.warn("Execution covered instruction list became empty. Proceeding with not filtered instruction list.") - } - } - - execution.copy( - coverage = Coverage( - coveredInstructions = filteredCoveredInstructions, - instructionsCount = coverage.instructionsCount, - missedInstructions = coverage.missedInstructions - ) - ) - } - } - - private fun createBuildDirsClassLoader(): URLClassLoader { - val urls = buildDirs.map { it.toURL() }.toTypedArray() - return URLClassLoader(urls, null) - } - - private fun hasAnnotations(className: String, annotations: List>): Boolean = - utContext - .classLoader - .loadClass(className) - .annotations - .any { existingAnnotation -> - annotations.any { annotation -> - annotation.isInstance(existingAnnotation) - } - } - - private fun doContextDependentPreparationForTestGeneration() { - when (applicationContext) { - is SpringApplicationContext -> when (applicationContext.springTestType) { - UNIT_TEST -> Unit - INTEGRATION_TEST -> - if (applicationContext.springContextLoadingResult == null) - // force pool to create an appropriate executor - applicationContext.springContextLoadingResult = ConcreteExecutor( - executionInstrumentation, - classpathForEngine - ).tryLoadingSpringContext() - } - else -> Unit - } + private fun loadConcreteExecutionContext(): ConcreteContextLoadingResult { + // force pool to create an appropriate executor + val concreteExecutor = ConcreteExecutor(concreteExecutionContext.instrumentationFactory, classpathForEngine) + return concreteExecutionContext.loadContext(concreteExecutor) } } - - diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/sarif/GenerateTestsAndSarifReportFacade.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/sarif/GenerateTestsAndSarifReportFacade.kt index ac50a76489..26547c212b 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/sarif/GenerateTestsAndSarifReportFacade.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/sarif/GenerateTestsAndSarifReportFacade.kt @@ -3,6 +3,7 @@ package org.utbot.framework.plugin.sarif import org.utbot.framework.codegen.domain.ForceStaticMocking import org.utbot.framework.codegen.domain.NoStaticMocking import org.utbot.framework.codegen.generator.CodeGenerator +import org.utbot.framework.codegen.generator.CodeGeneratorParams import org.utbot.framework.codegen.services.language.CgLanguageAssistant import org.utbot.framework.plugin.api.TestCaseGenerator import org.utbot.framework.plugin.api.UtMethodTestSet @@ -70,15 +71,17 @@ class GenerateTestsAndSarifReportFacade( val isForceStaticMocking = sarifProperties.forceStaticMocking == ForceStaticMocking.FORCE return CodeGenerator( - classUnderTest = targetClass.classUnderTest.id, - projectType = sarifProperties.projectType, - testFramework = sarifProperties.testFramework, - mockFramework = sarifProperties.mockFramework, - staticsMocking = sarifProperties.staticsMocking, - forceStaticMocking = sarifProperties.forceStaticMocking, - generateWarningsForStaticMocking = isNoStaticMocking && isForceStaticMocking, - codegenLanguage = sarifProperties.codegenLanguage, - cgLanguageAssistant = CgLanguageAssistant.getByCodegenLanguage(sarifProperties.codegenLanguage), + CodeGeneratorParams( + classUnderTest = targetClass.classUnderTest.id, + projectType = sarifProperties.projectType, + testFramework = sarifProperties.testFramework, + mockFramework = sarifProperties.mockFramework, + staticsMocking = sarifProperties.staticsMocking, + forceStaticMocking = sarifProperties.forceStaticMocking, + generateWarningsForStaticMocking = isNoStaticMocking && isForceStaticMocking, + codegenLanguage = sarifProperties.codegenLanguage, + cgLanguageAssistant = CgLanguageAssistant.getByCodegenLanguage(sarifProperties.codegenLanguage), + ) ) } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineProcessMain.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineProcessMain.kt index ead8820553..a2d54d6511 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineProcessMain.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineProcessMain.kt @@ -14,10 +14,10 @@ import org.utbot.framework.codegen.domain.ProjectType import org.utbot.framework.codegen.domain.RuntimeExceptionTestsBehaviour import org.utbot.framework.codegen.domain.testFrameworkByName import org.utbot.framework.codegen.generator.AbstractCodeGenerator -import org.utbot.framework.codegen.generator.CodeGenerator -import org.utbot.framework.codegen.generator.SpringCodeGenerator +import org.utbot.framework.codegen.generator.CodeGeneratorParams import org.utbot.framework.codegen.reports.TestsGenerationReport import org.utbot.framework.codegen.services.language.CgLanguageAssistant +import org.utbot.framework.context.ApplicationContext import org.utbot.framework.plugin.api.* import org.utbot.framework.plugin.api.MethodDescription import org.utbot.framework.plugin.api.util.UtContext @@ -289,7 +289,7 @@ private fun destinationWarningMessage(testPackageName: String?, classUnderTestPa } } -private fun createCodeGenerator(kryoHelper: KryoHelper, params: RenderParams, codeGenerationContext: CodeGenerationContext): AbstractCodeGenerator { +private fun createCodeGenerator(kryoHelper: KryoHelper, params: RenderParams, applicationContext: ApplicationContext): AbstractCodeGenerator { with(params) { val classUnderTest: ClassId = kryoHelper.readObject(classUnderTest) val paramNames: MutableMap> = kryoHelper.readObject(paramNames) @@ -298,61 +298,29 @@ private fun createCodeGenerator(kryoHelper: KryoHelper, params: RenderParams, co val forceStaticMocking: ForceStaticMocking = kryoHelper.readObject(forceStaticMocking) val projectType = ProjectType.valueOf(projectType) - return when (codeGenerationContext) { - is SpringCodeGenerationContext -> { - SpringCodeGenerator( - classUnderTest = classUnderTest, - projectType = projectType, - springCodeGenerationContext = codeGenerationContext, - generateUtilClassFile = generateUtilClassFile, - paramNames = paramNames, - testFramework = testFramework, - mockFramework = MockFramework.valueOf(mockFramework), - codegenLanguage = CodegenLanguage.valueOf(codegenLanguage), - cgLanguageAssistant = CgLanguageAssistant.getByCodegenLanguage( - CodegenLanguage.valueOf( - codegenLanguage - ) - ), - parameterizedTestSource = ParametrizedTestSource.valueOf(parameterizedTestSource), - staticsMocking = staticMocking, - forceStaticMocking = forceStaticMocking, - generateWarningsForStaticMocking = generateWarningsForStaticMocking, - runtimeExceptionTestsBehaviour = RuntimeExceptionTestsBehaviour.valueOf( - runtimeExceptionTestsBehaviour - ), - hangingTestsTimeout = HangingTestsTimeout(hangingTestsTimeout), - enableTestsTimeout = enableTestsTimeout, - testClassPackageName = testClassPackageName, + return applicationContext.createCodeGenerator(CodeGeneratorParams( + classUnderTest = classUnderTest, + projectType = projectType, + generateUtilClassFile = generateUtilClassFile, + paramNames = paramNames, + testFramework = testFramework, + mockFramework = MockFramework.valueOf(mockFramework), + codegenLanguage = CodegenLanguage.valueOf(codegenLanguage), + cgLanguageAssistant = CgLanguageAssistant.getByCodegenLanguage( + CodegenLanguage.valueOf( + codegenLanguage ) - } - - else -> { - CodeGenerator( - classUnderTest = classUnderTest, - projectType = projectType, - generateUtilClassFile = generateUtilClassFile, - paramNames = paramNames, - testFramework = testFramework, - mockFramework = MockFramework.valueOf(mockFramework), - codegenLanguage = CodegenLanguage.valueOf(codegenLanguage), - cgLanguageAssistant = CgLanguageAssistant.getByCodegenLanguage( - CodegenLanguage.valueOf( - codegenLanguage - ) - ), - parameterizedTestSource = ParametrizedTestSource.valueOf(parameterizedTestSource), - staticsMocking = staticMocking, - forceStaticMocking = forceStaticMocking, - generateWarningsForStaticMocking = generateWarningsForStaticMocking, - runtimeExceptionTestsBehaviour = RuntimeExceptionTestsBehaviour.valueOf( - runtimeExceptionTestsBehaviour - ), - hangingTestsTimeout = HangingTestsTimeout(hangingTestsTimeout), - enableTestsTimeout = enableTestsTimeout, - testClassPackageName = testClassPackageName, - ) - } - } + ), + parameterizedTestSource = ParametrizedTestSource.valueOf(parameterizedTestSource), + staticsMocking = staticMocking, + forceStaticMocking = forceStaticMocking, + generateWarningsForStaticMocking = generateWarningsForStaticMocking, + runtimeExceptionTestsBehaviour = RuntimeExceptionTestsBehaviour.valueOf( + runtimeExceptionTestsBehaviour + ), + hangingTestsTimeout = HangingTestsTimeout(hangingTestsTimeout), + enableTestsTimeout = enableTestsTimeout, + testClassPackageName = testClassPackageName, + )) } } \ No newline at end of file diff --git a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestConstructors.kt b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestConstructors.kt index 93801a9621..fea5acdb17 100644 --- a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestConstructors.kt +++ b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestConstructors.kt @@ -28,7 +28,7 @@ class TestConstructors { @Test fun testDefaultConstructor() { ConcreteExecutor( - InvokeInstrumentation(), + InvokeInstrumentation.Factory, CLASSPATH ).use { executor -> val constructors = ClassWithMultipleConstructors::class.constructors @@ -43,7 +43,7 @@ class TestConstructors { @Test fun testIntConstructors() { ConcreteExecutor( - InvokeInstrumentation(), + InvokeInstrumentation.Factory, CLASSPATH ).use { executor -> val constructors = ClassWithMultipleConstructors::class.constructors @@ -65,7 +65,7 @@ class TestConstructors { @Test fun testStringConstructors() { withInstrumentation( - InvokeInstrumentation(), + InvokeInstrumentation.Factory, CLASSPATH ) { executor -> val constructors = ClassWithMultipleConstructors::class.constructors @@ -86,7 +86,7 @@ class TestConstructors { @Test fun testCoverageConstructor() { withInstrumentation( - CoverageInstrumentation, + CoverageInstrumentation.Factory, CLASSPATH ) { executor -> val constructors = ClassWithMultipleConstructors::class.constructors @@ -106,7 +106,7 @@ class TestConstructors { @Test fun testExecutionTraceConstructor() { withInstrumentation( - ExecutionTraceInstrumentation(), + ExecutionTraceInstrumentation.Factory, CLASSPATH ) { executor -> val constructors = ClassWithMultipleConstructors::class.constructors diff --git a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestCoverageInstrumentation.kt b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestCoverageInstrumentation.kt index 34f9da3fd9..565ca75f12 100644 --- a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestCoverageInstrumentation.kt +++ b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestCoverageInstrumentation.kt @@ -24,7 +24,7 @@ class TestCoverageInstrumentation { @Test fun testCatchTargetException() { ConcreteExecutor( - CoverageInstrumentation, + CoverageInstrumentation.Factory, ExampleClass::class.java.protectionDomain.codeSource.location.path ).use { val testObject = ExampleClass() @@ -41,7 +41,7 @@ class TestCoverageInstrumentation { @Test fun testIfBranches() { ConcreteExecutor( - CoverageInstrumentation, + CoverageInstrumentation.Factory, ExampleClass::class.java.protectionDomain.codeSource.location.path ).use { val testObject = ExampleClass() @@ -63,7 +63,7 @@ class TestCoverageInstrumentation { @Test fun testWrongArgumentsException() { ConcreteExecutor( - CoverageInstrumentation, + CoverageInstrumentation.Factory, ExampleClass::class.java.protectionDomain.codeSource.location.path ).use { val testObject = ExampleClass() @@ -86,7 +86,7 @@ class TestCoverageInstrumentation { @Test fun testMultipleRunsInsideCoverage() { ConcreteExecutor( - CoverageInstrumentation, + CoverageInstrumentation.Factory, ExampleClass::class.java.protectionDomain.codeSource.location.path ).use { val testObject = ExampleClass() @@ -121,7 +121,7 @@ class TestCoverageInstrumentation { @Test fun testSameResult() { ConcreteExecutor( - CoverageInstrumentation, + CoverageInstrumentation.Factory, ExampleClass::class.java.protectionDomain.codeSource.location.path ).use { val testObject = ExampleClass() @@ -143,7 +143,7 @@ class TestCoverageInstrumentation { @Test fun testResult() { ConcreteExecutor( - CoverageInstrumentation, + CoverageInstrumentation.Factory, ExampleClass::class.java.protectionDomain.codeSource.location.path ).use { val testObject = ExampleClass() @@ -160,7 +160,7 @@ class TestCoverageInstrumentation { @Test fun testEmptyMethod() { ConcreteExecutor( - CoverageInstrumentation, + CoverageInstrumentation.Factory, ExampleClass::class.java.protectionDomain.codeSource.location.path ).use { val testObject = ExampleClass() @@ -176,7 +176,7 @@ class TestCoverageInstrumentation { @Test fun testTernaryOperator() { ConcreteExecutor( - CoverageInstrumentation, + CoverageInstrumentation.Factory, StaticSubstitutionExamples::class.java.protectionDomain.codeSource.location.path ).use { val testObject = StaticSubstitutionExamples() diff --git a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestInvokeInstrumentation.kt b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestInvokeInstrumentation.kt index 85ecde4416..a585b2f45f 100644 --- a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestInvokeInstrumentation.kt +++ b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestInvokeInstrumentation.kt @@ -21,7 +21,7 @@ class TestInvokeInstrumentation { @Test fun testCatchTargetException() { ConcreteExecutor( - InvokeInstrumentation(), + InvokeInstrumentation.Factory, ExampleClass::class.java.protectionDomain.codeSource.location.path ).use { @@ -36,7 +36,7 @@ class TestInvokeInstrumentation { @Test fun testWrongArgumentsException() { ConcreteExecutor( - InvokeInstrumentation(), + InvokeInstrumentation.Factory, ExampleClass::class.java.protectionDomain.codeSource.location.path ).use { val testObject = ExampleClass() @@ -57,7 +57,7 @@ class TestInvokeInstrumentation { @Test fun testSameResult() { ConcreteExecutor( - InvokeInstrumentation(), + InvokeInstrumentation.Factory, ExampleClass::class.java.protectionDomain.codeSource.location.path ).use { val testObject = ExampleClass() @@ -73,7 +73,7 @@ class TestInvokeInstrumentation { @Test fun testEmptyMethod() { ConcreteExecutor( - InvokeInstrumentation(), + InvokeInstrumentation.Factory, ExampleClass::class.java.protectionDomain.codeSource.location.path ).use { val testObject = ExampleClass() @@ -87,7 +87,7 @@ class TestInvokeInstrumentation { @Test fun testStaticMethodCall() { ConcreteExecutor( - InvokeInstrumentation(), + InvokeInstrumentation.Factory, StaticExampleClass::class.java.protectionDomain.codeSource.location.path ).use { val res1 = it.execute(StaticExampleClass::inc, arrayOf()) @@ -105,7 +105,7 @@ class TestInvokeInstrumentation { @Test fun testNullableMethod() { ConcreteExecutor( - InvokeInstrumentation(), + InvokeInstrumentation.Factory, StaticExampleClass::class.java.protectionDomain.codeSource.location.path ).use { val res1 = it.execute( @@ -136,7 +136,7 @@ class TestInvokeInstrumentation { @Test fun testDifferentSignaturesButSameMethodNames() { ConcreteExecutor( - InvokeInstrumentation(), + InvokeInstrumentation.Factory, ClassWithSameMethodNames::class.java.protectionDomain.codeSource.location.path ).use { val clazz = ClassWithSameMethodNames::class diff --git a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestInvokeWithStaticsInstrumentation.kt b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestInvokeWithStaticsInstrumentation.kt index b1865499e0..c06143e38f 100644 --- a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestInvokeWithStaticsInstrumentation.kt +++ b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestInvokeWithStaticsInstrumentation.kt @@ -35,7 +35,7 @@ class TestInvokeWithStaticsInstrumentation { @Test fun testIfBranches() { ConcreteExecutor( - InvokeWithStaticsInstrumentation(), + InvokeWithStaticsInstrumentation.Factory, CLASSPATH ).use { val res = it.execute(StaticExampleClass::inc, arrayOf(), null) @@ -52,7 +52,7 @@ class TestInvokeWithStaticsInstrumentation { @Test fun testHiddenClass1() { ConcreteExecutor( - InvokeWithStaticsInstrumentation(), + InvokeWithStaticsInstrumentation.Factory, CLASSPATH ).use { val res = it.execute(TestedClass::slomayInts, arrayOf(), null) @@ -71,7 +71,7 @@ class TestInvokeWithStaticsInstrumentation { @Test fun testHiddenClassRepeatCall() { ConcreteExecutor( - InvokeWithStaticsInstrumentation(), + InvokeWithStaticsInstrumentation.Factory, CLASSPATH ).use { val se = StaticEnvironment( @@ -89,7 +89,7 @@ class TestInvokeWithStaticsInstrumentation { @Test fun testReferenceEquality() { ConcreteExecutor( - InvokeWithStaticsInstrumentation(), + InvokeWithStaticsInstrumentation.Factory, CLASSPATH ).use { diff --git a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestIsolated.kt b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestIsolated.kt index 500da27859..96b3047f6e 100644 --- a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestIsolated.kt +++ b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestIsolated.kt @@ -21,7 +21,7 @@ class TestIsolated { fun testCatchTargetException() { val javaClass = ExampleClass::class.java ConcreteExecutor( - InvokeInstrumentation(), + InvokeInstrumentation.Factory, javaClass.protectionDomain.codeSource.location.path ).use { val testObject = ExampleClass() @@ -37,7 +37,7 @@ class TestIsolated { @Test fun testWrongArgumentsException() { ConcreteExecutor( - InvokeInstrumentation(), + InvokeInstrumentation.Factory, ExampleClass::class.java.protectionDomain.codeSource.location.path ).use { val testObject = ExampleClass() @@ -63,7 +63,7 @@ class TestIsolated { @Test fun testSameResult() { ConcreteExecutor( - InvokeInstrumentation(), + InvokeInstrumentation.Factory, ExampleClass::class.java.protectionDomain.codeSource.location.path ).use { val testObject = ExampleClass() @@ -81,7 +81,7 @@ class TestIsolated { @Test fun testEmptyMethod() { ConcreteExecutor( - InvokeInstrumentation(), + InvokeInstrumentation.Factory, ExampleClass::class.java.protectionDomain.codeSource.location.path ).use { val testObject = ExampleClass() @@ -97,7 +97,7 @@ class TestIsolated { @Test fun testStaticMethodCall() { ConcreteExecutor( - InvokeInstrumentation(), + InvokeInstrumentation.Factory, StaticExampleClass::class.java.protectionDomain.codeSource.location.path ).use { val isolatedFunctionInc = Isolated(StaticExampleClass::inc, it) @@ -116,7 +116,7 @@ class TestIsolated { @Test fun testNullableMethod() { ConcreteExecutor( - InvokeInstrumentation(), + InvokeInstrumentation.Factory, StaticExampleClass::class.java.protectionDomain.codeSource.location.path ).use { val isolatedFunction = Isolated(StaticExampleClass::canBeNull, it) diff --git a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestStaticMethods.kt b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestStaticMethods.kt index 6ad3bca179..0a75292636 100644 --- a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestStaticMethods.kt +++ b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestStaticMethods.kt @@ -17,7 +17,7 @@ class TestStaticMethods { @Test fun testStaticMethodCall() { ConcreteExecutor( - CoverageInstrumentation, + CoverageInstrumentation.Factory, StaticExampleClass::class.java.protectionDomain.codeSource.location.path ).use { val res1 = it.execute(StaticExampleClass::inc, arrayOf()) @@ -44,7 +44,7 @@ class TestStaticMethods { @Test fun testNullableMethod() { ConcreteExecutor( - CoverageInstrumentation, + CoverageInstrumentation.Factory, StaticExampleClass::class.java.protectionDomain.codeSource.location.path ).use { val res1 = it.execute( @@ -75,7 +75,7 @@ class TestStaticMethods { @Test fun testNullableMethodWithoutAnnotations() { ConcreteExecutor( - CoverageInstrumentation, + CoverageInstrumentation.Factory, StaticExampleClass::class.java.protectionDomain.codeSource.location.path ).use { val res1 = it.execute( diff --git a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestWithInstrumentation.kt b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestWithInstrumentation.kt index f49c010b81..ae00e0f7a8 100644 --- a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestWithInstrumentation.kt +++ b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/TestWithInstrumentation.kt @@ -20,7 +20,7 @@ class TestWithInstrumentation { @Test fun testStaticMethodCall() { withInstrumentation( - CoverageInstrumentation, + CoverageInstrumentation.Factory, StaticExampleClass::class.java.protectionDomain.codeSource.location.path ) { executor -> val res1 = executor.execute(StaticExampleClass::inc, arrayOf()) @@ -47,7 +47,7 @@ class TestWithInstrumentation { @Test fun testDifferentSignaturesButSameMethodNames() { withInstrumentation( - InvokeInstrumentation(), + InvokeInstrumentation.Factory, ClassWithSameMethodNames::class.java.protectionDomain.codeSource.location.path ) { executor -> val clazz = ClassWithSameMethodNames::class @@ -70,7 +70,7 @@ class TestWithInstrumentation { @Test fun testInnerClasses() { withInstrumentation( - CoverageInstrumentation, + CoverageInstrumentation.Factory, ClassWithInnerClasses::class.java.protectionDomain.codeSource.location.path ) { executor -> val innerClazz = ClassWithInnerClasses.InnerClass::class.java diff --git a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/benchmark/Benchmark.kt b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/benchmark/Benchmark.kt index 41ec9da126..d4e9ba2028 100644 --- a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/benchmark/Benchmark.kt +++ b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/benchmark/Benchmark.kt @@ -12,7 +12,7 @@ import org.junit.jupiter.api.Assertions.assertEquals fun getBasicCoverageTime(count: Int): Double { var time: Long ConcreteExecutor( - CoverageInstrumentation, + CoverageInstrumentation.Factory, Repeater::class.java.protectionDomain.codeSource.location.path ).use { executor -> val dc0 = Repeater(", ") @@ -51,7 +51,7 @@ fun getNativeCallTime(count: Int): Double { fun getJustResultTime(count: Int): Double { var time: Long ConcreteExecutor( - InvokeInstrumentation(), + InvokeInstrumentation.Factory, Repeater::class.java.protectionDomain.codeSource.location.path ).use { val dc0 = Repeater(", ") diff --git a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/benchmark/BenchmarkFibonacci.kt b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/benchmark/BenchmarkFibonacci.kt index 4c370553de..07d788e4ce 100644 --- a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/benchmark/BenchmarkFibonacci.kt +++ b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/benchmark/BenchmarkFibonacci.kt @@ -13,7 +13,7 @@ import kotlin.system.measureNanoTime fun getBasicCoverageTime_fib(count: Int): Double { var time: Long ConcreteExecutor( - CoverageInstrumentation, + CoverageInstrumentation.Factory, Fibonacci::class.java.protectionDomain.codeSource.location.path ).use { val fib = Isolated(Fibonacci::calc, it) @@ -47,7 +47,7 @@ fun getNativeCallTime_fib(count: Int): Double { fun getJustResultTime_fib(count: Int): Double { var time: Long ConcreteExecutor( - InvokeInstrumentation(), + InvokeInstrumentation.Factory, Fibonacci::class.java.protectionDomain.codeSource.location.path ).use { val fib = Isolated(Fibonacci::calc, it) diff --git a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/benchmark/TestBenchmarkClasses.kt b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/benchmark/TestBenchmarkClasses.kt index 81d8240500..f189907c5d 100644 --- a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/benchmark/TestBenchmarkClasses.kt +++ b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/benchmark/TestBenchmarkClasses.kt @@ -18,7 +18,7 @@ class TestBenchmarkClasses { @Disabled("Ask Sergey to check") fun testRepeater() { ConcreteExecutor( - CoverageInstrumentation, + CoverageInstrumentation.Factory, Repeater::class.java.protectionDomain.codeSource.location.path ).use { val dc0 = Repeater(", ") @@ -36,7 +36,7 @@ class TestBenchmarkClasses { @Test fun testFibonacci() { ConcreteExecutor( - InvokeInstrumentation(), + InvokeInstrumentation.Factory, Fibonacci::class.java.protectionDomain.codeSource.location.path ).use { val res = diff --git a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/et/TestMixedExTrace.kt b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/et/TestMixedExTrace.kt index 560a314633..c5ecefeded 100644 --- a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/et/TestMixedExTrace.kt +++ b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/et/TestMixedExTrace.kt @@ -25,7 +25,7 @@ class TestMixedExTrace { @Test fun testMixedDoesNotThrow() { ConcreteExecutor( - ExecutionTraceInstrumentation(), + ExecutionTraceInstrumentation.Factory, CLASSPATH ).use { val A = Isolated(ClassMixedWithNotInstrumented_Instr::a, it) diff --git a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/et/TestSimpleExTrace.kt b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/et/TestSimpleExTrace.kt index e7dab32d5f..f8b7224de0 100644 --- a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/et/TestSimpleExTrace.kt +++ b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/et/TestSimpleExTrace.kt @@ -34,7 +34,7 @@ class TestSimpleExTrace { @Test fun testClassSimple() { ConcreteExecutor( - ExecutionTraceInstrumentation(), + ExecutionTraceInstrumentation.Factory, CLASSPATH ).use { val alwaysThrows = Isolated(ClassSimple::alwaysThrows, it) @@ -89,7 +89,7 @@ class TestSimpleExTrace { @Test fun testClasSimpleCatch() { ConcreteExecutor( - ExecutionTraceInstrumentation(), + ExecutionTraceInstrumentation.Factory, CLASSPATH ).use { val A = Isolated(ClassSimpleCatch::A, it) @@ -159,7 +159,7 @@ class TestSimpleExTrace { @Test fun testClassSimpleRecursive() { ConcreteExecutor( - ExecutionTraceInstrumentation(), + ExecutionTraceInstrumentation.Factory, CLASSPATH ).use { val A = Isolated(ClassSimpleRecursive::A, it) @@ -228,7 +228,7 @@ class TestSimpleExTrace { @Test fun testClassBinaryRecursionWithTrickyThrow() { ConcreteExecutor( - ExecutionTraceInstrumentation(), + ExecutionTraceInstrumentation.Factory, CLASSPATH ).use { val A = Isolated(ClassBinaryRecursionWithTrickyThrow::A, it) @@ -347,7 +347,7 @@ class TestSimpleExTrace { @Test fun testClassBinaryRecursionWithThrow() { ConcreteExecutor( - ExecutionTraceInstrumentation(), + ExecutionTraceInstrumentation.Factory, CLASSPATH ).use { val A = Isolated(ClassBinaryRecursionWithThrow::A, it) @@ -436,7 +436,7 @@ class TestSimpleExTrace { @Test fun testClassSimpleNPE() { ConcreteExecutor( - ExecutionTraceInstrumentation(), + ExecutionTraceInstrumentation.Factory, CLASSPATH ).use { val A = Isolated(ClassSimpleNPE::A, it) diff --git a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/et/TestStaticsUsage.kt b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/et/TestStaticsUsage.kt index f4aef6ead6..22ade07a8c 100644 --- a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/et/TestStaticsUsage.kt +++ b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/et/TestStaticsUsage.kt @@ -31,7 +31,7 @@ class StaticsUsageDetectionTest { @Test fun testStaticsUsageOneUsage() { withInstrumentation( - ExecutionTraceInstrumentation(), + ExecutionTraceInstrumentation.Factory, ObjectWithStaticFieldsExample::class.java.protectionDomain.codeSource.location.path ) { val instance = ObjectWithStaticFieldsExample() @@ -46,7 +46,7 @@ class StaticsUsageDetectionTest { @Test fun testStaticsUsageZeroUsages() { withInstrumentation( - ExecutionTraceInstrumentation(), + ExecutionTraceInstrumentation.Factory, ObjectWithStaticFieldsExample::class.java.protectionDomain.codeSource.location.path ) { val instance = ObjectWithStaticFieldsExample() diff --git a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/jacoco/TestSameAsJaCoCo.kt b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/jacoco/TestSameAsJaCoCo.kt index de8051a775..a289e1d5fa 100644 --- a/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/jacoco/TestSameAsJaCoCo.kt +++ b/utbot-instrumentation-tests/src/test/kotlin/org/utbot/examples/jacoco/TestSameAsJaCoCo.kt @@ -123,7 +123,7 @@ private fun methodCoverageWithJaCoCo(kClass: KClass<*>, method: KCallable<*>, ex private fun methodCoverage(kClass: KClass<*>, method: KCallable<*>, executions: List): Pair { return withInstrumentation( - CoverageInstrumentation, + CoverageInstrumentation.Factory, kClass.java.protectionDomain.codeSource.location.path ) { executor -> for (execution in executions) { diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/ConcreteExecutor.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/ConcreteExecutor.kt index 90539abbf9..5f80edec97 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/ConcreteExecutor.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/ConcreteExecutor.kt @@ -20,7 +20,7 @@ import org.utbot.framework.plugin.api.InstrumentedProcessDeathException import org.utbot.common.logException import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.FieldId -import org.utbot.framework.plugin.api.SpringContextLoadingResult +import org.utbot.framework.plugin.api.ConcreteContextLoadingResult import org.utbot.framework.plugin.api.SpringRepositoryId import org.utbot.framework.plugin.api.util.UtContext import org.utbot.framework.plugin.api.util.signature @@ -48,10 +48,10 @@ private val logger = KotlinLogging.logger {} * @see [org.utbot.instrumentation.instrumentation.coverage.CoverageInstrumentation]. */ inline fun > withInstrumentation( - instrumentation: T, + instrumentationFactory: Instrumentation.Factory, pathsToUserClasses: String, block: (ConcreteExecutor) -> TBlockResult -) = ConcreteExecutor(instrumentation, pathsToUserClasses).use { +) = ConcreteExecutor(instrumentationFactory, pathsToUserClasses).use { block(it) } @@ -59,20 +59,20 @@ class ConcreteExecutorPool(val maxCount: Int = Settings.defaultConcreteExecutorP private val executors = ArrayDeque>(maxCount) /** - * Tries to find the concrete executor for the supplied [instrumentation] and [pathsToDependencyClasses]. If it + * Tries to find the concrete executor for the supplied [instrumentationFactory] and [pathsToDependencyClasses]. If it * doesn't exist, then creates a new one. */ fun > get( - instrumentation: TInstrumentation, + instrumentationFactory: Instrumentation.Factory, pathsToUserClasses: String, ): ConcreteExecutor { executors.removeIf { !it.alive } @Suppress("UNCHECKED_CAST") return executors.firstOrNull { - it.pathsToUserClasses == pathsToUserClasses && it.instrumentation == instrumentation + it.pathsToUserClasses == pathsToUserClasses && it.instrumentationFactory == instrumentationFactory } as? ConcreteExecutor - ?: ConcreteExecutor.createNew(instrumentation, pathsToUserClasses).apply { + ?: ConcreteExecutor.createNew(instrumentationFactory, pathsToUserClasses).apply { executors.addFirst(this) if (executors.size > maxCount) { executors.removeLast().close() @@ -100,12 +100,12 @@ class ConcreteExecutorPool(val maxCount: Int = Settings.defaultConcreteExecutorP * * If [instrumentation] depends on other classes, they should be passed in [pathsToDependencyClasses]. * - * Also takes [instrumentation] object which will be used in the instrumented process for the instrumentation. + * Also takes [instrumentationFactory] object which will be used in the instrumented process to create an [Instrumentation]. * - * @param TIResult the return type of [Instrumentation.invoke] function for the given [instrumentation]. + * @param TIResult the return type of [Instrumentation.invoke] function for the given [Instrumentation]. */ -class ConcreteExecutor> private constructor( - internal val instrumentation: TInstrumentation, +class ConcreteExecutor> private constructor( + internal val instrumentationFactory: Instrumentation.Factory, internal val pathsToUserClasses: String ) : Closeable, Executor { private val ldef: LifetimeDefinition = LifetimeDefinition() @@ -129,14 +129,14 @@ class ConcreteExecutor> p * and in case of failure, creates a new one. */ operator fun > invoke( - instrumentation: TInstrumentation, + instrumentationFactory: Instrumentation.Factory, pathsToUserClasses: String, - ) = defaultPool.get(instrumentation, pathsToUserClasses) + ) = defaultPool.get(instrumentationFactory, pathsToUserClasses) internal fun > createNew( - instrumentation: TInstrumentation, + instrumentationFactory: Instrumentation.Factory, pathsToUserClasses: String - ) = ConcreteExecutor(instrumentation, pathsToUserClasses) + ) = ConcreteExecutor(instrumentationFactory, pathsToUserClasses) } var classLoader: ClassLoader? = UtContext.currentContext()?.classLoader @@ -157,7 +157,7 @@ class ConcreteExecutor> p if (proc == null || !proc.lifetime.isAlive) { proc = InstrumentedProcess( ldef, - instrumentation, + instrumentationFactory, pathsToUserClasses, classLoader ) @@ -293,7 +293,7 @@ fun ConcreteExecutor<*, *>.getRelevantSpringRepositories(classId: ClassId): Set< } } -fun ConcreteExecutor<*, *>.tryLoadingSpringContext(): SpringContextLoadingResult = runBlocking { +fun ConcreteExecutor<*, *>.tryLoadingSpringContext(): ConcreteContextLoadingResult = runBlocking { withProcess { val result = instrumentedProcessModel.tryLoadingSpringContext.startSuspending(lifetime, Unit) kryoHelper.readObject(result.springContextLoadingResult) diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/Executor.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/Executor.kt index 4744cf765a..80213cb248 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/Executor.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/Executor.kt @@ -10,7 +10,7 @@ import kotlinx.coroutines.runBlocking * * @param TResult the type of an execution result. */ -interface Executor { +interface Executor { /** * Main method to override. * Returns the result of the execution of the [ExecutableId] with [arguments] and [parameters]. diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/Instrumentation.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/Instrumentation.kt index d6bd839734..4adcb58992 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/Instrumentation.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/Instrumentation.kt @@ -27,10 +27,9 @@ interface Instrumentation : ClassFileTransformer fun getStaticField(fieldId: FieldId): Result<*> - /** - * Will be called in the very beginning in the instrumented process. - * - * Do not call from engine process to avoid unwanted side effects (e.g. Spring context initialization) - */ - fun init(pathsToUserClasses: Set) {} + interface Factory> { + val additionalRuntimeClasspath: Set get() = emptySet() + + fun create(): TInstrumentation + } } \ No newline at end of file diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/InvokeInstrumentation.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/InvokeInstrumentation.kt index 730f73fd5b..96a1ef9109 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/InvokeInstrumentation.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/InvokeInstrumentation.kt @@ -117,4 +117,8 @@ class InvokeInstrumentation : Instrumentation> { protectionDomain: ProtectionDomain, classfileBuffer: ByteArray ) = null + + object Factory : Instrumentation.Factory, InvokeInstrumentation> { + override fun create(): InvokeInstrumentation = InvokeInstrumentation() + } } \ No newline at end of file diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/InvokeWithStaticsInstrumentation.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/InvokeWithStaticsInstrumentation.kt index 4b91be37fa..9572944aeb 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/InvokeWithStaticsInstrumentation.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/InvokeWithStaticsInstrumentation.kt @@ -89,6 +89,10 @@ class InvokeWithStaticsInstrumentation : Instrumentation> { .forEach { it.withAccessibility { it.set(null, savedFields[it.name]) } } } } + + object Factory : Instrumentation.Factory, InvokeWithStaticsInstrumentation> { + override fun create(): InvokeWithStaticsInstrumentation = InvokeWithStaticsInstrumentation() + } } private fun checkField(field: Field) = Modifier.isStatic(field.modifiers) diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/coverage/CoverageInstrumentation.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/coverage/CoverageInstrumentation.kt index bba4f864ca..ab3238a97b 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/coverage/CoverageInstrumentation.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/coverage/CoverageInstrumentation.kt @@ -22,7 +22,7 @@ data class CoverageInfo( /** * This instrumentation allows collecting coverage after several calls. */ -object CoverageInstrumentation : Instrumentation> { +class CoverageInstrumentation : Instrumentation> { private val invokeWithStatics = InvokeWithStaticsInstrumentation() /** @@ -94,6 +94,10 @@ object CoverageInstrumentation : Instrumentation> { return instrumenter.classByteCode } + + object Factory : Instrumentation.Factory, CoverageInstrumentation> { + override fun create(): CoverageInstrumentation = CoverageInstrumentation() + } } /** diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/et/ExecutionTraceInstrumentation.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/et/ExecutionTraceInstrumentation.kt index c10e90e73b..f62eff9b47 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/et/ExecutionTraceInstrumentation.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/et/ExecutionTraceInstrumentation.kt @@ -57,4 +57,8 @@ class ExecutionTraceInstrumentation : Instrumentation { classByteCode } } + + object Factory : Instrumentation.Factory { + override fun create(): ExecutionTraceInstrumentation = ExecutionTraceInstrumentation() + } } \ No newline at end of file diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/SimpleUtExecutionInstrumentation.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/SimpleUtExecutionInstrumentation.kt new file mode 100644 index 0000000000..7c21311731 --- /dev/null +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/SimpleUtExecutionInstrumentation.kt @@ -0,0 +1,174 @@ +package org.utbot.instrumentation.instrumentation.execution + +import org.utbot.framework.plugin.api.EnvironmentModels +import org.utbot.framework.plugin.api.FieldId +import org.utbot.framework.plugin.api.UtModel +import org.utbot.framework.plugin.api.util.singleExecutableId +import org.utbot.instrumentation.instrumentation.ArgumentList +import org.utbot.instrumentation.instrumentation.InvokeInstrumentation +import org.utbot.instrumentation.instrumentation.et.TraceHandler +import org.utbot.instrumentation.instrumentation.execution.constructors.ConstructOnlyUserClassesOrCachedObjectsStrategy +import org.utbot.instrumentation.instrumentation.execution.constructors.UtModelConstructor +import org.utbot.instrumentation.instrumentation.execution.context.InstrumentationContext +import org.utbot.instrumentation.instrumentation.execution.context.SimpleInstrumentationContext +import org.utbot.instrumentation.instrumentation.execution.ndd.NonDeterministicClassVisitor +import org.utbot.instrumentation.instrumentation.execution.ndd.NonDeterministicDetector +import org.utbot.instrumentation.instrumentation.execution.phases.PhasesController +import org.utbot.instrumentation.instrumentation.instrumenter.Instrumenter +import org.utbot.instrumentation.instrumentation.mock.MockClassVisitor +import java.security.ProtectionDomain +import kotlin.reflect.jvm.javaMethod + +class SimpleUtExecutionInstrumentation( + private val pathsToUserClasses: Set, + private val instrumentationContext: InstrumentationContext = SimpleInstrumentationContext() +) : UtExecutionInstrumentation { + private val delegateInstrumentation = InvokeInstrumentation() + + private val traceHandler = TraceHandler() + private val ndDetector = NonDeterministicDetector() + + /** + * Ignores [arguments], because concrete arguments will be constructed + * from models passed via [parameters]. + * + * Argument [parameters] must be of type [UtConcreteExecutionData]. + */ + override fun invoke( + clazz: Class<*>, + methodSignature: String, + arguments: ArgumentList, + parameters: Any?, + phasesWrapper: PhasesController.(invokeBasePhases: () -> UtConcreteExecutionResult) -> UtConcreteExecutionResult + ): UtConcreteExecutionResult { + if (parameters !is UtConcreteExecutionData) { + throw IllegalArgumentException("Argument parameters must be of type UtConcreteExecutionData, but was: ${parameters?.javaClass}") + } + val (stateBefore, instrumentations, timeout) = parameters // smart cast to UtConcreteExecutionData + + return PhasesController( + instrumentationContext, + traceHandler, + delegateInstrumentation, + timeout + ).computeConcreteExecutionResult { + phasesWrapper { + try { + // some preparation actions for concrete execution + val constructedData = applyPreprocessing(parameters) + + val (params, statics, cache) = constructedData + + // invocation + val concreteResult = executePhaseInTimeout(invocationPhase) { + invoke(clazz, methodSignature, params.map { it.value }) + } + + // statistics collection + val (coverage, ndResults) = executePhaseInTimeout(statisticsCollectionPhase) { + getCoverage(clazz) to getNonDeterministicResults() + } + + // model construction + val (executionResult, stateAfter, newInstrumentation) = executePhaseInTimeout(modelConstructionPhase) { + configureConstructor { + this.cache = cache + strategy = ConstructOnlyUserClassesOrCachedObjectsStrategy( + pathsToUserClasses, + cache + ) + } + + val ndStatics = constructStaticInstrumentation(ndResults.statics) + val ndNews = constructNewInstrumentation(ndResults.news, ndResults.calls) + val newInstrumentation = mergeInstrumentations(instrumentations, ndStatics, ndNews) + + val returnType = clazz.singleExecutableId(methodSignature).returnType + val executionResult = convertToExecutionResult(concreteResult, returnType) + + val stateAfterParametersWithThis = constructParameters(params) + val stateAfterStatics = constructStatics(stateBefore, statics) + val (stateAfterThis, stateAfterParameters) = if (stateBefore.thisInstance == null) { + null to stateAfterParametersWithThis + } else { + stateAfterParametersWithThis.first() to stateAfterParametersWithThis.drop(1) + } + val stateAfter = EnvironmentModels(stateAfterThis, stateAfterParameters, stateAfterStatics) + + Triple(executionResult, stateAfter, newInstrumentation) + } + + UtConcreteExecutionResult( + stateAfter, + executionResult, + coverage, + newInstrumentation + ) + } finally { + // restoring data after concrete execution + applyPostprocessing() + } + } + } + } + + override fun getStaticField(fieldId: FieldId): Result = + delegateInstrumentation.getStaticField(fieldId).map { value -> + UtModelConstructor.createOnlyUserClassesConstructor(pathsToUserClasses) + .construct(value, fieldId.type) + } + + override fun transform( + loader: ClassLoader?, + className: String, + classBeingRedefined: Class<*>?, + protectionDomain: ProtectionDomain, + classfileBuffer: ByteArray + ): ByteArray { + val instrumenter = Instrumenter(classfileBuffer, loader) + + traceHandler.registerClass(className) + instrumenter.visitInstructions(traceHandler.computeInstructionVisitor(className)) + + instrumenter.visitClass { writer -> + NonDeterministicClassVisitor(writer, ndDetector) + } + + val mockClassVisitor = instrumenter.visitClass { writer -> + MockClassVisitor( + writer, + InstrumentationContext.MockGetter::getMock.javaMethod!!, + InstrumentationContext.MockGetter::checkCallSite.javaMethod!!, + InstrumentationContext.MockGetter::hasMock.javaMethod!! + ) + } + + mockClassVisitor.signatureToId.forEach { (method, id) -> + instrumentationContext.methodSignatureToId += method to id + } + + return instrumenter.classByteCode + } + + class Factory( + private val pathsToUserClasses: Set + ) : UtExecutionInstrumentation.Factory { + override fun create(): UtExecutionInstrumentation = SimpleUtExecutionInstrumentation(pathsToUserClasses) + + override fun create(instrumentationContext: InstrumentationContext): UtExecutionInstrumentation = + SimpleUtExecutionInstrumentation(pathsToUserClasses, instrumentationContext) + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as Factory + + return pathsToUserClasses == other.pathsToUserClasses + } + + override fun hashCode(): Int { + return pathsToUserClasses.hashCode() + } + } +} diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/UtExecutionInstrumentation.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/UtExecutionInstrumentation.kt index 824393c1bb..adc9c180f9 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/UtExecutionInstrumentation.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/UtExecutionInstrumentation.kt @@ -2,22 +2,11 @@ package org.utbot.instrumentation.instrumentation.execution import org.utbot.framework.UtSettings import org.utbot.framework.plugin.api.* -import org.utbot.framework.plugin.api.util.singleExecutableId import org.utbot.instrumentation.instrumentation.ArgumentList import org.utbot.instrumentation.instrumentation.Instrumentation -import org.utbot.instrumentation.instrumentation.InvokeInstrumentation -import org.utbot.instrumentation.instrumentation.et.TraceHandler -import org.utbot.instrumentation.instrumentation.execution.constructors.ConstructOnlyUserClassesOrCachedObjectsStrategy -import org.utbot.instrumentation.instrumentation.execution.constructors.UtModelConstructor import org.utbot.instrumentation.instrumentation.execution.context.InstrumentationContext import org.utbot.instrumentation.instrumentation.execution.context.SimpleInstrumentationContext -import org.utbot.instrumentation.instrumentation.execution.ndd.NonDeterministicClassVisitor -import org.utbot.instrumentation.instrumentation.execution.ndd.NonDeterministicDetector import org.utbot.instrumentation.instrumentation.execution.phases.PhasesController -import org.utbot.instrumentation.instrumentation.instrumenter.Instrumenter -import org.utbot.instrumentation.instrumentation.mock.MockClassVisitor -import java.security.ProtectionDomain -import kotlin.reflect.jvm.javaMethod /** * Consists of the data needed to execute the method concretely. Also includes method arguments stored in models. @@ -47,34 +36,15 @@ data class UtConcreteExecutionResult( } } -// TODO if possible make it non singleton -object UtExecutionInstrumentation : Instrumentation { - private val delegateInstrumentation = InvokeInstrumentation() - - var instrumentationContext: InstrumentationContext = SimpleInstrumentationContext() - - private val traceHandler = TraceHandler() - private val ndDetector = NonDeterministicDetector() - private val pathsToUserClasses = mutableSetOf() - - override fun init(pathsToUserClasses: Set) { - UtExecutionInstrumentation.pathsToUserClasses.clear() - UtExecutionInstrumentation.pathsToUserClasses += pathsToUserClasses - } - - /** - * Ignores [arguments], because concrete arguments will be constructed - * from models passed via [parameters]. - * - * Argument [parameters] must be of type [UtConcreteExecutionData]. - */ +interface UtExecutionInstrumentation : Instrumentation { override fun invoke( clazz: Class<*>, methodSignature: String, arguments: ArgumentList, parameters: Any? - ): UtConcreteExecutionResult = - invoke(clazz, methodSignature, arguments, parameters, phasesWrapper = { it() }) + ): UtConcreteExecutionResult = invoke( + clazz, methodSignature, arguments, parameters, phasesWrapper = { invokeBasePhases -> invokeBasePhases() } + ) fun invoke( clazz: Class<*>, @@ -82,113 +52,11 @@ object UtExecutionInstrumentation : Instrumentation { arguments: ArgumentList, parameters: Any?, phasesWrapper: PhasesController.(invokeBasePhases: () -> UtConcreteExecutionResult) -> UtConcreteExecutionResult - ): UtConcreteExecutionResult { - if (parameters !is UtConcreteExecutionData) { - throw IllegalArgumentException("Argument parameters must be of type UtConcreteExecutionData, but was: ${parameters?.javaClass}") - } - val (stateBefore, instrumentations, timeout) = parameters // smart cast to UtConcreteExecutionData - - return PhasesController( - instrumentationContext, - traceHandler, - delegateInstrumentation, - timeout - ).computeConcreteExecutionResult { - phasesWrapper { - try { - // some preparation actions for concrete execution - val constructedData = applyPreprocessing(parameters) - - val (params, statics, cache) = constructedData - - // invocation - val concreteResult = executePhaseInTimeout(invocationPhase) { - invoke(clazz, methodSignature, params.map { it.value }) - } - - // statistics collection - val (coverage, ndResults) = executePhaseInTimeout(statisticsCollectionPhase) { - getCoverage(clazz) to getNonDeterministicResults() - } - - // model construction - val (executionResult, stateAfter, newInstrumentation) = executePhaseInTimeout(modelConstructionPhase) { - configureConstructor { - this.cache = cache - strategy = ConstructOnlyUserClassesOrCachedObjectsStrategy( - pathsToUserClasses, - cache - ) - } - - val ndStatics = constructStaticInstrumentation(ndResults.statics) - val ndNews = constructNewInstrumentation(ndResults.news, ndResults.calls) - val newInstrumentation = mergeInstrumentations(instrumentations, ndStatics, ndNews) - - val returnType = clazz.singleExecutableId(methodSignature).returnType - val executionResult = convertToExecutionResult(concreteResult, returnType) - - val stateAfterParametersWithThis = constructParameters(params) - val stateAfterStatics = constructStatics(stateBefore, statics) - val (stateAfterThis, stateAfterParameters) = if (stateBefore.thisInstance == null) { - null to stateAfterParametersWithThis - } else { - stateAfterParametersWithThis.first() to stateAfterParametersWithThis.drop(1) - } - val stateAfter = EnvironmentModels(stateAfterThis, stateAfterParameters, stateAfterStatics) - - Triple(executionResult, stateAfter, newInstrumentation) - } - - UtConcreteExecutionResult( - stateAfter, - executionResult, - coverage, - newInstrumentation - ) - } finally { - // restoring data after concrete execution - applyPostprocessing() - } - } - } - } - - override fun getStaticField(fieldId: FieldId): Result = - delegateInstrumentation.getStaticField(fieldId).map { value -> - UtModelConstructor.createOnlyUserClassesConstructor(pathsToUserClasses) - .construct(value, fieldId.type) - } - - override fun transform( - loader: ClassLoader?, - className: String, - classBeingRedefined: Class<*>?, - protectionDomain: ProtectionDomain, - classfileBuffer: ByteArray - ): ByteArray { - val instrumenter = Instrumenter(classfileBuffer, loader) - - traceHandler.registerClass(className) - instrumenter.visitInstructions(traceHandler.computeInstructionVisitor(className)) - - instrumenter.visitClass { writer -> - NonDeterministicClassVisitor(writer, ndDetector) - } - - val mockClassVisitor = instrumenter.visitClass { writer -> - MockClassVisitor( - writer, - InstrumentationContext.MockGetter::getMock.javaMethod!!, - InstrumentationContext.MockGetter::checkCallSite.javaMethod!!, - InstrumentationContext.MockGetter::hasMock.javaMethod!! - ) - } + ): UtConcreteExecutionResult - mockClassVisitor.signatureToId.forEach { (method, id) -> - instrumentationContext.methodSignatureToId += method to id - } + interface Factory : Instrumentation.Factory { + override fun create(): TInstrumentation = create(SimpleInstrumentationContext()) - return instrumenter.classByteCode + fun create(instrumentationContext: InstrumentationContext): TInstrumentation } } diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/SpringInstrumentationContext.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/SpringInstrumentationContext.kt index 9a43204246..eaf119b571 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/SpringInstrumentationContext.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/SpringInstrumentationContext.kt @@ -30,7 +30,7 @@ class SpringInstrumentationContext( ?: error("JavaConfiguration was expected, but ${springSettings.configuration.javaClass.name} was provided.") ) ), - profiles = springSettings.profiles, + profiles = springSettings.profiles.toTypedArray(), ) SpringApiProviderFacade diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/SpringUtExecutionInstrumentation.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/SpringUtExecutionInstrumentation.kt index a2f55b089d..b30c60d931 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/SpringUtExecutionInstrumentation.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/SpringUtExecutionInstrumentation.kt @@ -6,16 +6,17 @@ import org.utbot.common.JarUtils import org.utbot.common.hasOnClasspath import org.utbot.framework.plugin.api.BeanDefinitionData import org.utbot.framework.plugin.api.ClassId -import org.utbot.framework.plugin.api.SpringContextLoadingResult +import org.utbot.framework.plugin.api.FieldId +import org.utbot.framework.plugin.api.ConcreteContextLoadingResult import org.utbot.framework.plugin.api.SpringRepositoryId import org.utbot.framework.plugin.api.SpringSettings.* import org.utbot.framework.plugin.api.util.jClass import org.utbot.instrumentation.instrumentation.ArgumentList -import org.utbot.instrumentation.instrumentation.Instrumentation import org.utbot.instrumentation.instrumentation.execution.UtConcreteExecutionResult import org.utbot.instrumentation.instrumentation.execution.UtExecutionInstrumentation +import org.utbot.instrumentation.instrumentation.execution.context.InstrumentationContext import org.utbot.instrumentation.instrumentation.execution.phases.ExecutionPhaseFailingOnAnyException -import org.utbot.instrumentation.process.HandlerClassesLoader +import org.utbot.instrumentation.instrumentation.execution.phases.PhasesController import org.utbot.spring.api.SpringApi import java.net.URL import java.net.URLClassLoader @@ -25,14 +26,15 @@ import java.security.ProtectionDomain * UtExecutionInstrumentation wrapper that is aware of Spring configuration and profiles and initialises Spring context */ class SpringUtExecutionInstrumentation( - private val delegateInstrumentation: UtExecutionInstrumentation, - private val springSettings: PresentSpringSettings, + instrumentationContext: InstrumentationContext, + delegateInstrumentationFactory: UtExecutionInstrumentation.Factory<*>, + springSettings: PresentSpringSettings, private val beanDefinitions: List, - private val buildDirs: Array, -) : Instrumentation by delegateInstrumentation { - - private lateinit var instrumentationContext: SpringInstrumentationContext - private lateinit var userSourcesClassLoader: URLClassLoader + buildDirs: Array, +) : UtExecutionInstrumentation { + private val instrumentationContext = SpringInstrumentationContext(springSettings, instrumentationContext) + private val delegateInstrumentation = delegateInstrumentationFactory.create(this.instrumentationContext) + private val userSourcesClassLoader = URLClassLoader(buildDirs, null) private val relatedBeansCache = mutableMapOf, Set>() @@ -46,26 +48,9 @@ class SpringUtExecutionInstrumentation( private const val SPRING_COMMONS_JAR_FILENAME = "utbot-spring-commons-shadow.jar" } - override fun init(pathsToUserClasses: Set) { - HandlerClassesLoader.addUrls( - listOf( - JarUtils.extractJarFileFromResources( - jarFileName = SPRING_COMMONS_JAR_FILENAME, - jarResourcePath = "lib/$SPRING_COMMONS_JAR_FILENAME", - targetDirectoryName = "spring-commons" - ).path - ) - ) - - userSourcesClassLoader = URLClassLoader(buildDirs, null) - instrumentationContext = SpringInstrumentationContext(springSettings, delegateInstrumentation.instrumentationContext) - delegateInstrumentation.instrumentationContext = instrumentationContext - delegateInstrumentation.init(pathsToUserClasses) - } - - fun tryLoadingSpringContext(): SpringContextLoadingResult { + fun tryLoadingSpringContext(): ConcreteContextLoadingResult { val apiProviderResult = instrumentationContext.springApiProviderResult - return SpringContextLoadingResult( + return ConcreteContextLoadingResult( contextLoaded = apiProviderResult.result.isSuccess, exceptions = apiProviderResult.exceptions ) @@ -75,25 +60,30 @@ class SpringUtExecutionInstrumentation( clazz: Class<*>, methodSignature: String, arguments: ArgumentList, - parameters: Any? + parameters: Any?, + phasesWrapper: PhasesController.(invokeBasePhases: () -> UtConcreteExecutionResult) -> UtConcreteExecutionResult ): UtConcreteExecutionResult { getRelevantBeans(clazz).forEach { beanName -> springApi.resetBean(beanName) } return delegateInstrumentation.invoke(clazz, methodSignature, arguments, parameters) { invokeBasePhases -> - // NB! beforeTestMethod() and afterTestMethod() are intentionally called inside phases, - // so they are executed in one thread with method under test - // NB! beforeTestMethod() and afterTestMethod() are executed without timeout, because: - // - if the invokeBasePhases() times out, we still want to execute afterTestMethod() - // - first call to beforeTestMethod() can take significant amount of time due to class loading & transformation - executePhaseWithoutTimeout(SpringBeforeTestMethodPhase) { springApi.beforeTestMethod() } - try { - invokeBasePhases() - } finally { - executePhaseWithoutTimeout(SpringAfterTestMethodPhase) { springApi.afterTestMethod() } + phasesWrapper { + // NB! beforeTestMethod() and afterTestMethod() are intentionally called inside phases, + // so they are executed in one thread with method under test + // NB! beforeTestMethod() and afterTestMethod() are executed without timeout, because: + // - if the invokeBasePhases() times out, we still want to execute afterTestMethod() + // - first call to beforeTestMethod() can take significant amount of time due to class loading & transformation + executePhaseWithoutTimeout(SpringBeforeTestMethodPhase) { springApi.beforeTestMethod() } + try { + invokeBasePhases() + } finally { + executePhaseWithoutTimeout(SpringAfterTestMethodPhase) { springApi.afterTestMethod() } + } } } } + override fun getStaticField(fieldId: FieldId): Result<*> = delegateInstrumentation.getStaticField(fieldId) + private fun getRelevantBeans(clazz: Class<*>): Set = relatedBeansCache.getOrPut(clazz) { beanDefinitions .filter { it.beanTypeName == clazz.name } @@ -136,4 +126,47 @@ class SpringUtExecutionInstrumentation( } else { null } + + class Factory( + private val delegateInstrumentationFactory: UtExecutionInstrumentation.Factory<*>, + private val springSettings: PresentSpringSettings, + private val beanDefinitions: List, + private val buildDirs: Array, + ) : UtExecutionInstrumentation.Factory { + override val additionalRuntimeClasspath: Set + get() = super.additionalRuntimeClasspath + JarUtils.extractJarFileFromResources( + jarFileName = SPRING_COMMONS_JAR_FILENAME, + jarResourcePath = "lib/$SPRING_COMMONS_JAR_FILENAME", + targetDirectoryName = "spring-commons" + ).path + + override fun create(instrumentationContext: InstrumentationContext): SpringUtExecutionInstrumentation = + SpringUtExecutionInstrumentation( + instrumentationContext, + delegateInstrumentationFactory, + springSettings, + beanDefinitions, + buildDirs + ) + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as Factory + + if (delegateInstrumentationFactory != other.delegateInstrumentationFactory) return false + if (springSettings != other.springSettings) return false + if (beanDefinitions != other.beanDefinitions) return false + return buildDirs.contentEquals(other.buildDirs) + } + + override fun hashCode(): Int { + var result = delegateInstrumentationFactory.hashCode() + result = 31 * result + springSettings.hashCode() + result = 31 * result + beanDefinitions.hashCode() + result = 31 * result + buildDirs.contentHashCode() + return result + } + } } \ No newline at end of file diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/InstrumentedProcessMain.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/InstrumentedProcessMain.kt index 70b64339c0..58618ac593 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/InstrumentedProcessMain.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/InstrumentedProcessMain.kt @@ -144,11 +144,12 @@ private fun InstrumentedProcessModel.setup(kryoHelper: KryoHelper, watchdog: Idl } watchdog.measureTimeForActiveCall(setInstrumentation, "Instrumentation setup") { params -> logger.debug { "setInstrumentation request" } - instrumentation = kryoHelper.readObject(params.instrumentation) + val instrumentationFactory = kryoHelper.readObject>(params.instrumentation) + HandlerClassesLoader.addUrls(instrumentationFactory.additionalRuntimeClasspath) + instrumentation = instrumentationFactory.create() logger.debug { "instrumentation - ${instrumentation.javaClass.name} " } Agent.dynamicClassTransformer.transformer = instrumentation Agent.dynamicClassTransformer.addUserPaths(pathsToUserClasses) - instrumentation.init(pathsToUserClasses) } watchdog.measureTimeForActiveCall(addPaths, "User and dependency classpath setup") { params -> pathsToUserClasses = params.pathsToUserClasses.split(File.pathSeparatorChar).toSet() diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/InstrumentedProcess.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/InstrumentedProcess.kt index 7ec82ef285..baef39ac5f 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/InstrumentedProcess.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/InstrumentedProcess.kt @@ -88,7 +88,7 @@ class InstrumentedProcess private constructor( suspend operator fun > invoke( parent: Lifetime, - instrumentation: TInstrumentation, + instrumentationFactory: Instrumentation.Factory, pathsToUserClasses: String, classLoader: ClassLoader? ): InstrumentedProcess = parent.createNested().terminateOnException { lifetime -> @@ -131,7 +131,7 @@ class InstrumentedProcess private constructor( logger.trace("sending instrumentation") proc.instrumentedProcessModel.setInstrumentation.startSuspending( proc.lifetime, SetInstrumentationParams( - proc.kryoHelper.writeObject(instrumentation) + proc.kryoHelper.writeObject(instrumentationFactory) ) ) logger.trace("start commands sent") diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/CodeGenerationController.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/CodeGenerationController.kt index 9399ae1279..b58ce9595c 100644 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/CodeGenerationController.kt +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/CodeGenerationController.kt @@ -71,7 +71,6 @@ import org.utbot.framework.codegen.domain.StaticImport import org.utbot.framework.codegen.tree.ututils.UtilClassKind import org.utbot.framework.codegen.tree.ututils.UtilClassKind.Companion.UT_UTILS_INSTANCE_NAME import org.utbot.framework.plugin.api.ClassId -import org.utbot.framework.plugin.api.CodeGenerationContext import org.utbot.framework.plugin.api.CodegenLanguage import org.utbot.intellij.plugin.inspection.UnitTestBotInspectionManager import org.utbot.intellij.plugin.models.GenerateTestsModel @@ -116,7 +115,6 @@ object CodeGenerationController { fun generateTests( model: GenerateTestsModel, - codeGenerationContext: CodeGenerationContext, classesWithTests: Map, psi2KClass: Map, process: EngineProcess, @@ -163,7 +161,6 @@ object CodeGenerationController { testFilePointer, srcClassPathToSarifReport, model, - codeGenerationContext, latch, utilClassListener, indicator @@ -667,7 +664,6 @@ object CodeGenerationController { filePointer: SmartPsiElementPointer, srcClassPathToSarifReport: MutableMap, model: GenerateTestsModel, - codeGenerationContext: CodeGenerationContext, reportsCountDown: CountDownLatch, utilClassListener: UtilClassListener, indicator: ProgressIndicator diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/UtTestsDialogProcessor.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/UtTestsDialogProcessor.kt index cd8524ceeb..87e0da4865 100644 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/UtTestsDialogProcessor.kt +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/UtTestsDialogProcessor.kt @@ -48,6 +48,9 @@ import org.utbot.framework.CancellationStrategyType.NONE import org.utbot.framework.CancellationStrategyType.SAVE_PROCESSED_RESULTS import org.utbot.framework.UtSettings import org.utbot.framework.codegen.domain.ProjectType.* +import org.utbot.framework.context.simple.SimpleApplicationContext +import org.utbot.framework.context.simple.SimpleMockerContext +import org.utbot.framework.context.spring.SpringApplicationContextImpl import org.utbot.framework.plugin.api.* import org.utbot.framework.plugin.api.SpringSettings.* import org.utbot.framework.plugin.api.SpringConfiguration.* @@ -260,6 +263,9 @@ object UtTestsDialogProcessor { process.terminateOnException { _ -> val classpathForClassLoader = buildDirs + classpathList process.setupUtContext(classpathForClassLoader) + val simpleApplicationContext = SimpleApplicationContext( + SimpleMockerContext(mockFrameworkInstalled, staticMockingConfigured) + ) val applicationContext = when (model.projectType) { Spring -> { val beanDefinitions = @@ -273,21 +279,17 @@ object UtTestsDialogProcessor { } } - val shouldUseImplementors = beanDefinitions.isNotEmpty() - val clarifiedBeanDefinitions = clarifyBeanDefinitionReturnTypes(beanDefinitions, project) - SpringApplicationContext( - mockFrameworkInstalled, - staticMockingConfigured, + SpringApplicationContextImpl( + simpleApplicationContext, clarifiedBeanDefinitions, - shouldUseImplementors, model.springTestType, model.springSettings, ) } - else -> ApplicationContext(mockFrameworkInstalled, staticMockingConfigured) + else -> simpleApplicationContext } process.createTestGenerator( buildDirs, @@ -444,7 +446,7 @@ object UtTestsDialogProcessor { // indicator.checkCanceled() invokeLater { - generateTests(model, applicationContext, testSetsByClass, psi2KClass, process, indicator) + generateTests(model, testSetsByClass, psi2KClass, process, indicator) logger.info { "Generation complete" } } } diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt index b6fdbf62a2..221cacad88 100644 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt @@ -17,6 +17,7 @@ import org.utbot.framework.plugin.api.SpringSettings.* import org.utbot.common.* import org.utbot.framework.UtSettings import org.utbot.framework.codegen.tree.ututils.UtilClassKind +import org.utbot.framework.context.ApplicationContext import org.utbot.framework.plugin.api.* import org.utbot.framework.plugin.api.BeanAdditionalData import org.utbot.framework.plugin.api.BeanDefinitionData diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/GenerateTestsDialogWindow.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/GenerateTestsDialogWindow.kt index 9e621d1fad..547ab7090b 100644 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/GenerateTestsDialogWindow.kt +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/GenerateTestsDialogWindow.kt @@ -694,7 +694,7 @@ class GenerateTestsDialogWindow(val model: GenerateTestsModel) : DialogWrapper(m model.springSettings = when (springConfig.item) { - NO_SPRING_CONFIGURATION_OPTION -> AbsentSpringSettings() + NO_SPRING_CONFIGURATION_OPTION -> AbsentSpringSettings else -> { val shortConfigName = springConfig.item.toString() val config = @@ -708,7 +708,7 @@ class GenerateTestsDialogWindow(val model: GenerateTestsModel) : DialogWrapper(m PresentSpringSettings( configuration = config, - profiles = parseProfileExpression(profileNames.text, DEFAULT_SPRING_PROFILE_NAME) + profiles = parseProfileExpression(profileNames.text, DEFAULT_SPRING_PROFILE_NAME).toList() ) } } diff --git a/utbot-js/src/main/kotlin/framework/codegen/JsCgLanguageAssistant.kt b/utbot-js/src/main/kotlin/framework/codegen/JsCgLanguageAssistant.kt index fd8e2a47dc..59e1f79d6c 100644 --- a/utbot-js/src/main/kotlin/framework/codegen/JsCgLanguageAssistant.kt +++ b/utbot-js/src/main/kotlin/framework/codegen/JsCgLanguageAssistant.kt @@ -10,11 +10,11 @@ import org.utbot.framework.codegen.domain.context.TestClassContext import org.utbot.framework.codegen.renderer.CgAbstractRenderer import org.utbot.framework.codegen.renderer.CgPrinter import org.utbot.framework.codegen.renderer.CgRendererContext -import org.utbot.framework.codegen.services.language.CgLanguageAssistant +import org.utbot.framework.codegen.services.language.AbstractCgLanguageAssistant import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.utils.testClassNameGenerator -object JsCgLanguageAssistant : CgLanguageAssistant() { +object JsCgLanguageAssistant : AbstractCgLanguageAssistant() { override val outerMostTestClassContent: TestClassContext = TestClassContext() diff --git a/utbot-junit-contest/src/main/kotlin/org/utbot/contest/Contest.kt b/utbot-junit-contest/src/main/kotlin/org/utbot/contest/Contest.kt index e0812048a0..896cd35002 100644 --- a/utbot-junit-contest/src/main/kotlin/org/utbot/contest/Contest.kt +++ b/utbot-junit-contest/src/main/kotlin/org/utbot/contest/Contest.kt @@ -50,6 +50,7 @@ import kotlinx.coroutines.withTimeoutOrNull import org.utbot.framework.SummariesGenerationType import org.utbot.framework.codegen.domain.* import org.utbot.framework.codegen.generator.CodeGenerator +import org.utbot.framework.codegen.generator.CodeGeneratorParams import org.utbot.framework.codegen.services.language.CgLanguageAssistant import org.utbot.framework.minimization.minimizeExecutions import org.utbot.framework.plugin.api.* @@ -218,6 +219,7 @@ fun runGeneration( val statsForClass = StatsForClass(project, cut.fqn) val codeGenerator = CodeGenerator( + CodeGeneratorParams( cut.classId, projectType = ProjectType.PureJvm, testFramework = junitByVersion(junitVersion), @@ -227,6 +229,7 @@ fun runGeneration( cgLanguageAssistant = CgLanguageAssistant.getByCodegenLanguage(CodegenLanguage.defaultItem), runtimeExceptionTestsBehaviour = RuntimeExceptionTestsBehaviour.PASS, ) + ) logger.info().measureTime({ "class ${cut.fqn}" }, { statsForClass }) { diff --git a/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/PythonCgLanguageAssistant.kt b/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/PythonCgLanguageAssistant.kt index b6befec04d..7fbfa5b617 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/PythonCgLanguageAssistant.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/PythonCgLanguageAssistant.kt @@ -5,7 +5,7 @@ import org.utbot.framework.codegen.domain.models.CgVariable import org.utbot.framework.codegen.renderer.CgPrinter import org.utbot.framework.codegen.renderer.CgAbstractRenderer import org.utbot.framework.codegen.renderer.CgRendererContext -import org.utbot.framework.codegen.services.language.CgLanguageAssistant +import org.utbot.framework.codegen.services.language.AbstractCgLanguageAssistant import org.utbot.framework.plugin.api.ClassId import org.utbot.python.framework.api.python.PythonTree import org.utbot.python.framework.codegen.model.constructor.name.PythonCgNameGenerator @@ -16,8 +16,7 @@ import org.utbot.python.framework.codegen.model.constructor.tree.PythonCgVariabl import org.utbot.python.framework.codegen.model.constructor.visitor.CgPythonRenderer import org.utbot.python.framework.codegen.model.services.access.PythonCgFieldStateManager -object PythonCgLanguageAssistant : CgLanguageAssistant() { - +object PythonCgLanguageAssistant : AbstractCgLanguageAssistant() { override val extension: String get() = ".py" 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 fc809207a0..878dc54be4 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 @@ -14,6 +14,7 @@ import org.utbot.framework.codegen.domain.models.CgMethodTestSet import org.utbot.framework.codegen.domain.models.CgVariable import org.utbot.framework.codegen.domain.models.SimpleTestClassModel import org.utbot.framework.codegen.generator.CodeGenerator +import org.utbot.framework.codegen.generator.CodeGeneratorParams import org.utbot.framework.codegen.renderer.CgAbstractRenderer import org.utbot.framework.codegen.renderer.CgPrinterImpl import org.utbot.framework.codegen.renderer.CgRendererContext @@ -59,21 +60,23 @@ class PythonCodeGenerator( enableTestsTimeout: Boolean = true, testClassPackageName: String = classUnderTest.packageName ) : CodeGenerator( - classUnderTest = classUnderTest, - projectType = ProjectType.Python, - paramNames = paramNames, - generateUtilClassFile = true, - testFramework = testFramework, - mockFramework = mockFramework, - staticsMocking = staticsMocking, - forceStaticMocking = forceStaticMocking, - generateWarningsForStaticMocking = generateWarningsForStaticMocking, - parameterizedTestSource = parameterizedTestSource, - runtimeExceptionTestsBehaviour = runtimeExceptionTestsBehaviour, - hangingTestsTimeout = hangingTestsTimeout, - enableTestsTimeout = enableTestsTimeout, - testClassPackageName = testClassPackageName, - cgLanguageAssistant = PythonCgLanguageAssistant, + CodeGeneratorParams( + classUnderTest = classUnderTest, + projectType = ProjectType.Python, + paramNames = paramNames, + generateUtilClassFile = true, + testFramework = testFramework, + mockFramework = mockFramework, + staticsMocking = staticsMocking, + forceStaticMocking = forceStaticMocking, + generateWarningsForStaticMocking = generateWarningsForStaticMocking, + parameterizedTestSource = parameterizedTestSource, + runtimeExceptionTestsBehaviour = runtimeExceptionTestsBehaviour, + hangingTestsTimeout = hangingTestsTimeout, + enableTestsTimeout = enableTestsTimeout, + testClassPackageName = testClassPackageName, + cgLanguageAssistant = PythonCgLanguageAssistant, + ) ) { fun pythonGenerateAsStringWithTestReport( cgTestSets: List, diff --git a/utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/analyzer/SpringApplicationAnalyzer.kt b/utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/analyzer/SpringApplicationAnalyzer.kt index 706ad79efd..ba81bff3c1 100644 --- a/utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/analyzer/SpringApplicationAnalyzer.kt +++ b/utbot-spring-analyzer/src/main/kotlin/org/utbot/spring/analyzer/SpringApplicationAnalyzer.kt @@ -14,7 +14,7 @@ class SpringApplicationAnalyzer { val configurationClasses = SourceFinder(applicationData).findSources() val instantiationSettings = InstantiationSettings( configurationClasses, - applicationData.springSettings.profiles, + applicationData.springSettings.profiles.toTypedArray(), ) return SpringApiProviderFacade.getInstance(this::class.java.classLoader) diff --git a/utbot-spring-commons-api/src/main/kotlin/org/utbot/spring/api/UTSpringContextLoadingException.kt b/utbot-spring-commons-api/src/main/kotlin/org/utbot/spring/api/UTSpringContextLoadingException.kt index 471c706497..d8bbf2b716 100644 --- a/utbot-spring-commons-api/src/main/kotlin/org/utbot/spring/api/UTSpringContextLoadingException.kt +++ b/utbot-spring-commons-api/src/main/kotlin/org/utbot/spring/api/UTSpringContextLoadingException.kt @@ -6,6 +6,6 @@ package org.utbot.spring.api * and parts of stack trace inside Spring and user application. */ class UTSpringContextLoadingException(override val cause: Throwable) : Exception( - "UTBot failed to load Spring application context", + "Failed to load Spring application context", cause ) diff --git a/utbot-spring-test/src/test/kotlin/org/utbot/examples/spring/autowiring/oneBeanForOneType/ServiceWithInjectedAndNonInjectedFieldTests.kt b/utbot-spring-test/src/test/kotlin/org/utbot/examples/spring/autowiring/oneBeanForOneType/ServiceWithInjectedAndNonInjectedFieldTests.kt index 266c999150..ad94545d4b 100644 --- a/utbot-spring-test/src/test/kotlin/org/utbot/examples/spring/autowiring/oneBeanForOneType/ServiceWithInjectedAndNonInjectedFieldTests.kt +++ b/utbot-spring-test/src/test/kotlin/org/utbot/examples/spring/autowiring/oneBeanForOneType/ServiceWithInjectedAndNonInjectedFieldTests.kt @@ -4,19 +4,14 @@ import org.junit.jupiter.api.Test import org.utbot.examples.spring.utils.findAllRepositoryCall import org.utbot.examples.spring.utils.springAdditionalDependencies import org.utbot.examples.spring.utils.springMockStrategy -import org.utbot.examples.spring.utils.standardSpringTestingConfigurations -import org.utbot.framework.plugin.api.MockStrategyApi -import org.utbot.testcheckers.eq import org.utbot.testing.DoNotCalculate -import org.utbot.testing.UtValueTestCaseChecker import org.utbot.testing.ignoreExecutionsNumber import org.utbot.testing.isException import org.utbot.testing.singleMock import org.utbot.testing.value -internal class ServiceWithInjectedAndNonInjectedFieldTests: UtValueTestCaseChecker( +internal class ServiceWithInjectedAndNonInjectedFieldTests : SpringNoConfigUtValueTestCaseChecker( testClass = ServiceWithInjectedAndNonInjectedField::class, - configurations = standardSpringTestingConfigurations ) { @Test fun testGetOrdersSize() { diff --git a/utbot-spring-test/src/test/kotlin/org/utbot/examples/spring/autowiring/oneBeanForOneType/ServiceWithInjectedFieldTests.kt b/utbot-spring-test/src/test/kotlin/org/utbot/examples/spring/autowiring/oneBeanForOneType/ServiceWithInjectedFieldTests.kt index 20c59bb9e8..b051d491f6 100644 --- a/utbot-spring-test/src/test/kotlin/org/utbot/examples/spring/autowiring/oneBeanForOneType/ServiceWithInjectedFieldTests.kt +++ b/utbot-spring-test/src/test/kotlin/org/utbot/examples/spring/autowiring/oneBeanForOneType/ServiceWithInjectedFieldTests.kt @@ -5,14 +5,11 @@ import org.utbot.examples.spring.utils.findAllRepositoryCall import org.utbot.examples.spring.utils.saveRepositoryCall import org.utbot.examples.spring.utils.springAdditionalDependencies import org.utbot.examples.spring.utils.springMockStrategy -import org.utbot.examples.spring.utils.standardSpringTestingConfigurations -import org.utbot.framework.plugin.api.MockStrategyApi import org.utbot.testcheckers.eq import org.utbot.testing.* -internal class ServiceWithInjectedFieldTests : UtValueTestCaseChecker( +internal class ServiceWithInjectedFieldTests : SpringNoConfigUtValueTestCaseChecker( testClass = ServiceWithInjectedField::class, - configurations = standardSpringTestingConfigurations ) { @Test fun testGetOrders() { diff --git a/utbot-spring-test/src/test/kotlin/org/utbot/examples/spring/autowiring/oneBeanForOneType/SpringNoConfigUtValueTestCaseChecker.kt b/utbot-spring-test/src/test/kotlin/org/utbot/examples/spring/autowiring/oneBeanForOneType/SpringNoConfigUtValueTestCaseChecker.kt new file mode 100644 index 0000000000..3e0e66241b --- /dev/null +++ b/utbot-spring-test/src/test/kotlin/org/utbot/examples/spring/autowiring/oneBeanForOneType/SpringNoConfigUtValueTestCaseChecker.kt @@ -0,0 +1,14 @@ +package org.utbot.examples.spring.autowiring.oneBeanForOneType + +import org.utbot.examples.spring.utils.standardSpringTestingConfigurations +import org.utbot.testing.UtValueTestCaseChecker +import org.utbot.testing.springNoConfigApplicationContext +import kotlin.reflect.KClass + +abstract class SpringNoConfigUtValueTestCaseChecker( + testClass: KClass<*> +) : UtValueTestCaseChecker( + testClass, + configurations = standardSpringTestingConfigurations, + applicationContext = springNoConfigApplicationContext +) \ No newline at end of file diff --git a/utbot-spring-test/src/test/kotlin/org/utbot/examples/spring/utils/SpringTestingConfiguration.kt b/utbot-spring-test/src/test/kotlin/org/utbot/examples/spring/utils/SpringTestingConfiguration.kt index d311a46dde..3a967775a1 100644 --- a/utbot-spring-test/src/test/kotlin/org/utbot/examples/spring/utils/SpringTestingConfiguration.kt +++ b/utbot-spring-test/src/test/kotlin/org/utbot/examples/spring/utils/SpringTestingConfiguration.kt @@ -5,9 +5,6 @@ import org.springframework.data.repository.PagingAndSortingRepository import org.utbot.framework.codegen.domain.ParametrizedTestSource import org.utbot.framework.plugin.api.CodegenLanguage import org.utbot.framework.plugin.api.MockStrategyApi -import org.utbot.framework.plugin.api.SpringApplicationContext -import org.utbot.framework.plugin.api.SpringSettings -import org.utbot.framework.plugin.api.SpringTestType import org.utbot.testing.SpringConfiguration import org.utbot.testing.TestExecution diff --git a/utbot-testing/src/main/kotlin/org/utbot/testing/CodeGenerationIntegrationTest.kt b/utbot-testing/src/main/kotlin/org/utbot/testing/CodeGenerationIntegrationTest.kt index 3fabd147ff..021a5d6417 100644 --- a/utbot-testing/src/main/kotlin/org/utbot/testing/CodeGenerationIntegrationTest.kt +++ b/utbot-testing/src/main/kotlin/org/utbot/testing/CodeGenerationIntegrationTest.kt @@ -22,6 +22,7 @@ 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.domain.ParametrizedTestSource +import org.utbot.framework.context.ApplicationContext import java.nio.file.Path @TestInstance(TestInstance.Lifecycle.PER_CLASS) @@ -31,6 +32,7 @@ abstract class CodeGenerationIntegrationTest( private val testClass: KClass<*>, private var testCodeGeneration: Boolean = true, private val configurationsToTest: List, + private val applicationContext: ApplicationContext = defaultApplicationContext, ) { private val testSets: MutableList = arrayListOf() @@ -102,7 +104,7 @@ abstract class CodeGenerationIntegrationTest( ) val pipelineConfig = TestCodeGeneratorPipeline.configurePipeline(configuration) - TestCodeGeneratorPipeline(pipelineConfig).runClassesCodeGenerationTests(classStages) + TestCodeGeneratorPipeline(pipelineConfig, applicationContext).runClassesCodeGenerationTests(classStages) } catch (e: RuntimeException) { logger.warn(e) { "error in test pipeline" } pipelineErrors.add(e.message) diff --git a/utbot-testing/src/main/kotlin/org/utbot/testing/Configurations.kt b/utbot-testing/src/main/kotlin/org/utbot/testing/Configurations.kt index 8fe673c418..d468673abd 100644 --- a/utbot-testing/src/main/kotlin/org/utbot/testing/Configurations.kt +++ b/utbot-testing/src/main/kotlin/org/utbot/testing/Configurations.kt @@ -2,34 +2,56 @@ package org.utbot.testing import org.utbot.framework.codegen.domain.ParametrizedTestSource import org.utbot.framework.codegen.domain.ProjectType +import org.utbot.framework.context.simple.SimpleApplicationContext +import org.utbot.framework.context.simple.SimpleMockerContext +import org.utbot.framework.context.spring.SpringApplicationContextImpl import org.utbot.framework.plugin.api.CodegenLanguage -import org.utbot.framework.plugin.api.SpringApplicationContext +import org.utbot.framework.plugin.api.MockStrategyApi import org.utbot.framework.plugin.api.SpringSettings import org.utbot.framework.plugin.api.SpringTestType -abstract class AbstractConfiguration( - val projectType: ProjectType, - open val language: CodegenLanguage, - open val parametrizedTestSource: ParametrizedTestSource, - open val lastStage: Stage, -) +interface AbstractConfiguration { + val projectType: ProjectType + val mockStrategy: MockStrategyApi + val language: CodegenLanguage + val parametrizedTestSource: ParametrizedTestSource + val lastStage: Stage +} data class Configuration( override val language: CodegenLanguage, override val parametrizedTestSource: ParametrizedTestSource, override val lastStage: Stage, -): AbstractConfiguration(ProjectType.PureJvm, language, parametrizedTestSource, lastStage) +): AbstractConfiguration { + override val projectType: ProjectType + get() = ProjectType.PureJvm + + override val mockStrategy: MockStrategyApi + get() = MockStrategyApi.defaultItem +} data class SpringConfiguration( override val language: CodegenLanguage, override val parametrizedTestSource: ParametrizedTestSource, override val lastStage: Stage, -): AbstractConfiguration(ProjectType.Spring, language, parametrizedTestSource, lastStage) +): AbstractConfiguration { + override val projectType: ProjectType + get() = ProjectType.Spring + + override val mockStrategy: MockStrategyApi + get() = MockStrategyApi.springDefaultItem +} -val defaultSpringApplicationContext = SpringApplicationContext( - mockInstalled = true, - staticsMockingIsConfigured = true, - shouldUseImplementors = false, +val defaultApplicationContext = SimpleApplicationContext( + SimpleMockerContext( + mockFrameworkInstalled = true, + staticsMockingIsConfigured = true, + ) +) + +val springNoConfigApplicationContext = SpringApplicationContextImpl( + delegateContext = defaultApplicationContext, springTestType = SpringTestType.UNIT_TEST, - springSettings = SpringSettings.AbsentSpringSettings(), -) \ No newline at end of file + springSettings = SpringSettings.AbsentSpringSettings, + beanDefinitions = emptyList() +) diff --git a/utbot-testing/src/main/kotlin/org/utbot/testing/TestCodeGeneratorPipeline.kt b/utbot-testing/src/main/kotlin/org/utbot/testing/TestCodeGeneratorPipeline.kt index 1e9c66f417..4e1d16a386 100644 --- a/utbot-testing/src/main/kotlin/org/utbot/testing/TestCodeGeneratorPipeline.kt +++ b/utbot-testing/src/main/kotlin/org/utbot/testing/TestCodeGeneratorPipeline.kt @@ -2,21 +2,19 @@ package org.utbot.testing import mu.KotlinLogging import org.junit.jupiter.api.Assertions.assertFalse -import org.junit.jupiter.api.Assertions.assertTrue import org.utbot.common.FileUtil import org.utbot.common.measureTime import org.utbot.common.info import org.utbot.framework.codegen.generator.CodeGeneratorResult import org.utbot.framework.codegen.domain.ForceStaticMocking import org.utbot.framework.codegen.domain.ParametrizedTestSource -import org.utbot.framework.codegen.domain.ProjectType import org.utbot.framework.codegen.domain.StaticsMocking import org.utbot.framework.codegen.domain.TestFramework -import org.utbot.framework.codegen.generator.CodeGenerator -import org.utbot.framework.codegen.generator.SpringCodeGenerator +import org.utbot.framework.codegen.generator.CodeGeneratorParams import org.utbot.framework.codegen.services.language.CgLanguageAssistant import org.utbot.framework.codegen.tree.ututils.UtilClassKind import org.utbot.framework.codegen.tree.ututils.UtilClassKind.Companion.UT_UTILS_INSTANCE_NAME +import org.utbot.framework.context.ApplicationContext import org.utbot.framework.plugin.api.* import org.utbot.framework.plugin.api.util.UtContext import org.utbot.framework.plugin.api.util.description @@ -28,7 +26,10 @@ import kotlin.reflect.KClass internal val logger = KotlinLogging.logger {} -class TestCodeGeneratorPipeline(private val testInfrastructureConfiguration: TestInfrastructureConfiguration) { +class TestCodeGeneratorPipeline( + private val testInfrastructureConfiguration: TestInfrastructureConfiguration, + private val applicationContext: ApplicationContext +) { fun runClassesCodeGenerationTests(classesStages: ClassStages) { val pipeline = with(classesStages) { @@ -253,10 +254,10 @@ class TestCodeGeneratorPipeline(private val testInfrastructureConfiguration: Tes withUtContext(UtContext(classUnderTest.java.classLoader)) { val codeGenerator = with(testInfrastructureConfiguration) { - when (projectType) { - ProjectType.Spring -> SpringCodeGenerator( + applicationContext.createCodeGenerator( + CodeGeneratorParams( classUnderTest.id, - projectType = ProjectType.Spring, + projectType = projectType, generateUtilClassFile = generateUtilClassFile, paramNames = params, testFramework = testFramework, @@ -268,25 +269,8 @@ class TestCodeGeneratorPipeline(private val testInfrastructureConfiguration: Tes parameterizedTestSource = parametrizedTestSource, runtimeExceptionTestsBehaviour = runtimeExceptionTestsBehaviour, enableTestsTimeout = enableTestsTimeout, - springCodeGenerationContext = defaultSpringApplicationContext, ) - ProjectType.PureJvm -> CodeGenerator( - classUnderTest.id, - projectType = ProjectType.PureJvm, - generateUtilClassFile = generateUtilClassFile, - paramNames = params, - testFramework = testFramework, - staticsMocking = staticsMocking, - forceStaticMocking = forceStaticMocking, - generateWarningsForStaticMocking = false, - codegenLanguage = codegenLanguage, - cgLanguageAssistant = CgLanguageAssistant.getByCodegenLanguage(codegenLanguage), - parameterizedTestSource = parametrizedTestSource, - runtimeExceptionTestsBehaviour = runtimeExceptionTestsBehaviour, - enableTestsTimeout = enableTestsTimeout - ) - else -> error("Unsupported project type $projectType in code generator instantiation") - } + ) } val testClassCustomName = "${classUnderTest.java.simpleName}GeneratedTest" @@ -332,10 +316,7 @@ class TestCodeGeneratorPipeline(private val testInfrastructureConfiguration: Tes testFramework = TestFramework.defaultItem, codegenLanguage = configuration.language, mockFramework = MockFramework.defaultItem, - mockStrategy = when (configuration.projectType) { - ProjectType.Spring -> MockStrategyApi.springDefaultItem - else -> MockStrategyApi.defaultItem - }, + mockStrategy = configuration.mockStrategy, staticsMocking = StaticsMocking.defaultItem, parametrizedTestSource = configuration.parametrizedTestSource, forceStaticMocking = ForceStaticMocking.defaultItem, diff --git a/utbot-testing/src/main/kotlin/org/utbot/testing/TestSpecificTestCaseGenerator.kt b/utbot-testing/src/main/kotlin/org/utbot/testing/TestSpecificTestCaseGenerator.kt index 1cadbad0b1..4f2af2dccd 100644 --- a/utbot-testing/src/main/kotlin/org/utbot/testing/TestSpecificTestCaseGenerator.kt +++ b/utbot-testing/src/main/kotlin/org/utbot/testing/TestSpecificTestCaseGenerator.kt @@ -9,11 +9,10 @@ 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.plugin.api.ApplicationContext +import org.utbot.framework.context.ApplicationContext import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.ExecutableId import org.utbot.framework.plugin.api.MockStrategyApi -import org.utbot.framework.plugin.api.SpringApplicationContext import org.utbot.framework.plugin.api.TestCaseGenerator import org.utbot.framework.plugin.api.UtError import org.utbot.framework.plugin.api.UtExecution @@ -37,7 +36,7 @@ class TestSpecificTestCaseGenerator( engineActions: MutableList<(UtBotSymbolicEngine) -> Unit> = mutableListOf(), isCanceled: () -> Boolean = { false }, private val taintConfigurationProvider: TaintConfigurationProvider? = null, - applicationContext: ApplicationContext = ApplicationContext(), + applicationContext: ApplicationContext = defaultApplicationContext, ): TestCaseGenerator( listOf(buildDir), classpath, diff --git a/utbot-testing/src/main/kotlin/org/utbot/testing/UtValueTestCaseChecker.kt b/utbot-testing/src/main/kotlin/org/utbot/testing/UtValueTestCaseChecker.kt index 8a25900d8c..bc77d9de81 100644 --- a/utbot-testing/src/main/kotlin/org/utbot/testing/UtValueTestCaseChecker.kt +++ b/utbot-testing/src/main/kotlin/org/utbot/testing/UtValueTestCaseChecker.kt @@ -11,11 +11,11 @@ import org.utbot.engine.prettify import org.utbot.framework.SummariesGenerationType import org.utbot.framework.UtSettings import org.utbot.framework.UtSettings.daysLimitForTempFiles +import org.utbot.framework.context.ApplicationContext import org.utbot.framework.coverage.Coverage import org.utbot.framework.coverage.counters import org.utbot.framework.coverage.methodCoverage import org.utbot.framework.coverage.toAtLeast -import org.utbot.framework.plugin.api.ApplicationContext import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.ExecutableId import org.utbot.framework.plugin.api.FieldId @@ -26,9 +26,6 @@ import org.utbot.framework.plugin.api.MockStrategyApi import org.utbot.framework.plugin.api.MockStrategyApi.NO_MOCKS import org.utbot.framework.plugin.api.ObjectMockTarget import org.utbot.framework.plugin.api.ParameterMockTarget -import org.utbot.framework.plugin.api.SpringApplicationContext -import org.utbot.framework.plugin.api.SpringSettings -import org.utbot.framework.plugin.api.SpringTestType import org.utbot.framework.plugin.api.UtCompositeModel import org.utbot.framework.plugin.api.UtConcreteValue import org.utbot.framework.plugin.api.UtInstrumentation @@ -66,6 +63,7 @@ abstract class UtValueTestCaseChecker( testClass: KClass<*>, testCodeGeneration: Boolean = true, val configurations: List = standardTestingConfigurations, + val applicationContext: ApplicationContext = defaultApplicationContext, ) : CodeGenerationIntegrationTest(testClass, testCodeGeneration, configurations) { // contains already analyzed by the engine methods private val analyzedMethods: MutableMap = mutableMapOf() @@ -2018,7 +2016,7 @@ abstract class UtValueTestCaseChecker( ) val classStages = ClassStages(classUnderTest, stageStatusCheck, listOf(testSet)) - TestCodeGeneratorPipeline(testInfrastructureConfiguration).runClassesCodeGenerationTests(classStages) + TestCodeGeneratorPipeline(testInfrastructureConfiguration, applicationContext).runClassesCodeGenerationTests(classStages) } } } @@ -2163,11 +2161,7 @@ abstract class UtValueTestCaseChecker( buildInfo.buildDir, buildInfo.dependencyPath, System.getProperty("java.class.path"), - applicationContext = if (configurations.any { it is SpringConfiguration }) { - defaultSpringApplicationContext - } else { - ApplicationContext() - } + applicationContext = applicationContext ) } diff --git a/utbot-testing/src/main/kotlin/org/utbot/testing/Utils.kt b/utbot-testing/src/main/kotlin/org/utbot/testing/Utils.kt index a912eb9791..60ced254e9 100644 --- a/utbot-testing/src/main/kotlin/org/utbot/testing/Utils.kt +++ b/utbot-testing/src/main/kotlin/org/utbot/testing/Utils.kt @@ -1,8 +1,5 @@ package org.utbot.testing -import org.utbot.framework.plugin.api.SpringApplicationContext -import org.utbot.framework.plugin.api.SpringSettings -import org.utbot.framework.plugin.api.SpringTestType import org.utbot.framework.plugin.api.UtExecutionFailure import org.utbot.framework.plugin.api.UtExecutionResult import org.utbot.framework.plugin.api.UtExecutionSuccess