From e0fde478904cac94ec6de52b2be77ba7b6aa43b6 Mon Sep 17 00:00:00 2001 From: IlyaMuravjov Date: Thu, 13 Jul 2023 10:52:30 +0300 Subject: [PATCH 01/10] Move `ApplicationContext` and `SpringApplicationContext` to `utbot-framework` --- .../org/utbot/framework/plugin/api/Api.kt | 208 ------------------ .../src/main/kotlin/org/utbot/engine/Mocks.kt | 2 +- .../main/kotlin/org/utbot/engine/Traverser.kt | 2 +- .../org/utbot/engine/UtBotSymbolicEngine.kt | 2 + .../framework/context/ApplicationContext.kt | 75 +++++++ .../context/SpringApplicationContext.kt | 158 +++++++++++++ .../framework/plugin/api/TestCaseGenerator.kt | 2 + .../framework/process/EngineProcessMain.kt | 1 + .../generator/UtTestsDialogProcessor.kt | 2 + .../intellij/plugin/process/EngineProcess.kt | 1 + .../testing/TestCodeGeneratorPipeline.kt | 2 +- 11 files changed, 244 insertions(+), 211 deletions(-) create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/context/ApplicationContext.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/context/SpringApplicationContext.kt 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 99d71bc9da..e663eb6db5 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 @@ -8,7 +8,6 @@ package org.utbot.framework.plugin.api -import mu.KotlinLogging import org.utbot.common.FileUtil import org.utbot.common.isDefaultValue import org.utbot.common.withToStringThreadLocalReentrancyGuard @@ -56,17 +55,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 @@ -1332,71 +1322,6 @@ interface SpringCodeGenerationContext : CodeGenerationContext { 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) @@ -1429,139 +1354,6 @@ class SpringContextLoadingResult( 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 - - 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 } - } -} - enum class SpringTestType( override val id: String, override val displayName: String, 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..bf81618295 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.ApplicationContext import org.utbot.framework.plugin.api.util.isInaccessibleViaReflection import soot.BooleanType import soot.RefType 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..102050e024 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,7 @@ 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.plugin.api.ClassId import org.utbot.framework.plugin.api.ExecutableId import org.utbot.framework.plugin.api.FieldId 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 d552d32c14..56bfd7530b 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt @@ -33,6 +33,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.SpringApplicationContext import org.utbot.framework.plugin.api.* import org.utbot.framework.plugin.api.Step import org.utbot.framework.plugin.api.util.* 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..28f54155df --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/ApplicationContext.kt @@ -0,0 +1,75 @@ +package org.utbot.framework.context + +import org.utbot.framework.UtSettings +import org.utbot.framework.isFromTrustedLibrary +import org.utbot.framework.plugin.api.ClassId +import org.utbot.framework.plugin.api.CodeGenerationContext +import org.utbot.framework.plugin.api.TypeReplacementMode +import org.utbot.framework.plugin.api.UtError +import soot.RefType +import soot.SootField + +/** + * 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 = 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() +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/context/SpringApplicationContext.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/context/SpringApplicationContext.kt new file mode 100644 index 0000000000..adae9ea3be --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/SpringApplicationContext.kt @@ -0,0 +1,158 @@ +package org.utbot.framework.context + +import mu.KotlinLogging +import org.utbot.common.isAbstract +import org.utbot.common.isStatic +import org.utbot.framework.plugin.api.BeanDefinitionData +import org.utbot.framework.plugin.api.ClassId +import org.utbot.framework.plugin.api.SpringCodeGenerationContext +import org.utbot.framework.plugin.api.SpringContextLoadingResult +import org.utbot.framework.plugin.api.SpringSettings +import org.utbot.framework.plugin.api.SpringTestType +import org.utbot.framework.plugin.api.TypeReplacementMode +import org.utbot.framework.plugin.api.UtError +import org.utbot.framework.plugin.api.classId +import org.utbot.framework.plugin.api.id +import org.utbot.framework.plugin.api.isAbstractType +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.id +import org.utbot.framework.plugin.api.util.isSubtypeOf +import org.utbot.framework.plugin.api.util.jClass +import org.utbot.framework.plugin.api.util.utContext +import soot.RefType +import soot.SootField + +/** + * 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 + + 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) TypeReplacementMode.KnownImplementor else TypeReplacementMode.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 } + } +} \ No newline at end of file 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..5bd87ea982 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 @@ -25,6 +25,8 @@ 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.SpringApplicationContext import org.utbot.framework.plugin.api.utils.checkFrameworkDependencies import org.utbot.framework.minimization.minimizeTestCase import org.utbot.framework.plugin.api.util.SpringModelUtils.entityClassIds 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..ae1fdd32c1 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 @@ -18,6 +18,7 @@ import org.utbot.framework.codegen.generator.CodeGenerator import org.utbot.framework.codegen.generator.SpringCodeGenerator 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 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..fe18e24719 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,8 @@ 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.ApplicationContext +import org.utbot.framework.context.SpringApplicationContext import org.utbot.framework.plugin.api.* import org.utbot.framework.plugin.api.SpringSettings.* import org.utbot.framework.plugin.api.SpringConfiguration.* 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-testing/src/main/kotlin/org/utbot/testing/TestCodeGeneratorPipeline.kt b/utbot-testing/src/main/kotlin/org/utbot/testing/TestCodeGeneratorPipeline.kt index c48c7f6f24..102e5adeee 100644 --- a/utbot-testing/src/main/kotlin/org/utbot/testing/TestCodeGeneratorPipeline.kt +++ b/utbot-testing/src/main/kotlin/org/utbot/testing/TestCodeGeneratorPipeline.kt @@ -2,7 +2,6 @@ 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 @@ -17,6 +16,7 @@ import org.utbot.framework.codegen.generator.SpringCodeGenerator 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.SpringApplicationContext import org.utbot.framework.plugin.api.* import org.utbot.framework.plugin.api.util.UtContext import org.utbot.framework.plugin.api.util.description From 6e65b46197f618d73e82c048f4c99f84c64e1064 Mon Sep 17 00:00:00 2001 From: IlyaMuravjov Date: Thu, 13 Jul 2023 10:59:25 +0300 Subject: [PATCH 02/10] Merge `avoidSpeculativeNotNullChecks` and `speculativelyCannotProduceNullPointerException` into one method --- .../src/main/kotlin/org/utbot/engine/Traverser.kt | 8 ++------ .../utbot/framework/context/ApplicationContext.kt | 14 ++++---------- .../framework/context/SpringApplicationContext.kt | 2 -- 3 files changed, 6 insertions(+), 18 deletions(-) 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 102050e024..91f2efe7c4 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt @@ -2336,12 +2336,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 (applicationContext.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/framework/context/ApplicationContext.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/context/ApplicationContext.kt index 28f54155df..f7f50be9e0 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/context/ApplicationContext.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/ApplicationContext.kt @@ -49,15 +49,6 @@ open class ApplicationContext( */ 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). @@ -67,7 +58,10 @@ open class ApplicationContext( open fun speculativelyCannotProduceNullPointerException( field: SootField, classUnderTest: ClassId, - ): Boolean = field.isFinal || !field.isPublic + ): Boolean = + !UtSettings.maximizeCoverageUsingReflection && + field.declaringClass.isFromTrustedLibrary() && + (field.isFinal || !field.isPublic) open fun preventsFurtherTestGeneration(): Boolean = false diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/context/SpringApplicationContext.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/context/SpringApplicationContext.kt index adae9ea3be..ccf2b1b2cd 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/context/SpringApplicationContext.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/SpringApplicationContext.kt @@ -123,8 +123,6 @@ class SpringApplicationContext( 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. From c1d22e7286e231ccc6cc75ec3755e776c9e9e12e Mon Sep 17 00:00:00 2001 From: IlyaMuravjov Date: Thu, 13 Jul 2023 11:09:49 +0300 Subject: [PATCH 03/10] Extract `ApplicationContext` interface --- .../framework/context/ApplicationContext.kt | 48 +++------------- .../context/SimpleApplicationContext.kt | 55 +++++++++++++++++++ .../context/SpringApplicationContext.kt | 9 ++- .../framework/plugin/api/TestCaseGenerator.kt | 3 +- .../generator/UtTestsDialogProcessor.kt | 7 ++- .../testing/TestCodeGeneratorPipeline.kt | 4 +- 6 files changed, 76 insertions(+), 50 deletions(-) create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/context/SimpleApplicationContext.kt 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 index f7f50be9e0..22dee50a0e 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/context/ApplicationContext.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/ApplicationContext.kt @@ -1,7 +1,5 @@ package org.utbot.framework.context -import org.utbot.framework.UtSettings -import org.utbot.framework.isFromTrustedLibrary import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.CodeGenerationContext import org.utbot.framework.plugin.api.TypeReplacementMode @@ -9,45 +7,20 @@ import org.utbot.framework.plugin.api.UtError import soot.RefType import soot.SootField -/** - * 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 - } - } +interface ApplicationContext : CodeGenerationContext { + val mockFrameworkInstalled: Boolean + val staticsMockingIsConfigured: Boolean /** * Shows if there are any restrictions on type implementors. */ - open val typeReplacementMode: TypeReplacementMode = TypeReplacementMode.AnyImplementor + val typeReplacementMode: TypeReplacementMode /** * Finds a type to replace the original abstract type * if it is guided with some additional information. */ - open fun replaceTypeIfNeeded(type: RefType): ClassId? = null + fun replaceTypeIfNeeded(type: RefType): ClassId? /** * Checks whether accessing [field] (with a method invocation or field access) speculatively @@ -55,15 +28,12 @@ open class ApplicationContext( * * @see docs/SpeculativeFieldNonNullability.md for more information. */ - open fun speculativelyCannotProduceNullPointerException( + fun speculativelyCannotProduceNullPointerException( field: SootField, classUnderTest: ClassId, - ): Boolean = - !UtSettings.maximizeCoverageUsingReflection && - field.declaringClass.isFromTrustedLibrary() && - (field.isFinal || !field.isPublic) + ): Boolean - open fun preventsFurtherTestGeneration(): Boolean = false + fun preventsFurtherTestGeneration(): Boolean - open fun getErrors(): List = emptyList() + fun getErrors(): List } \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/context/SimpleApplicationContext.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/context/SimpleApplicationContext.kt new file mode 100644 index 0000000000..623b95a3cb --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/SimpleApplicationContext.kt @@ -0,0 +1,55 @@ +package org.utbot.framework.context + +import org.utbot.framework.UtSettings +import org.utbot.framework.isFromTrustedLibrary +import org.utbot.framework.plugin.api.ClassId +import org.utbot.framework.plugin.api.TypeReplacementMode +import org.utbot.framework.plugin.api.UtError +import soot.RefType +import soot.SootField + +/** + * 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 + */ +class SimpleApplicationContext( + override val mockFrameworkInstalled: Boolean = true, + staticsMockingIsConfigured: Boolean = true, +) : ApplicationContext { + override 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 + } + } + + override val typeReplacementMode: TypeReplacementMode = TypeReplacementMode.AnyImplementor + + override fun replaceTypeIfNeeded(type: RefType): ClassId? = null + + override fun speculativelyCannotProduceNullPointerException( + field: SootField, + classUnderTest: ClassId, + ): Boolean = + !UtSettings.maximizeCoverageUsingReflection && + field.declaringClass.isFromTrustedLibrary() && + (field.isFinal || !field.isPublic) + + override fun preventsFurtherTestGeneration(): Boolean = false + + override fun getErrors(): List = emptyList() +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/context/SpringApplicationContext.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/context/SpringApplicationContext.kt index ccf2b1b2cd..2b80dd52d6 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/context/SpringApplicationContext.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/SpringApplicationContext.kt @@ -41,13 +41,12 @@ import soot.SootField // 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, + private val delegateContext: ApplicationContext, val beanDefinitions: List = emptyList(), private val shouldUseImplementors: Boolean, override val springTestType: SpringTestType, override val springSettings: SpringSettings, -): ApplicationContext(mockInstalled, staticsMockingIsConfigured), SpringCodeGenerationContext { +): ApplicationContext by delegateContext, SpringCodeGenerationContext { override var springContextLoadingResult: SpringContextLoadingResult? = null @@ -136,7 +135,7 @@ class SpringApplicationContext( ): Boolean = field.fieldId in classUnderTest.allDeclaredFieldIds && field.type.classId !in allInjectedTypes override fun preventsFurtherTestGeneration(): Boolean = - super.preventsFurtherTestGeneration() || springContextLoadingResult?.contextLoaded == false + delegateContext.preventsFurtherTestGeneration() || springContextLoadingResult?.contextLoaded == false override fun getErrors(): List = springContextLoadingResult?.exceptions?.map { exception -> @@ -144,7 +143,7 @@ class SpringApplicationContext( "Failed to load Spring application context", exception ) - }.orEmpty() + super.getErrors() + }.orEmpty() + delegateContext.getErrors() fun getBeansAssignableTo(classId: ClassId): List = beanDefinitions.filter { beanDef -> // some bean classes may fail to load 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 5bd87ea982..bc9e9bdf45 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 @@ -26,6 +26,7 @@ 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.SimpleApplicationContext import org.utbot.framework.context.SpringApplicationContext import org.utbot.framework.plugin.api.utils.checkFrameworkDependencies import org.utbot.framework.minimization.minimizeTestCase @@ -70,7 +71,7 @@ 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(), ) { private val logger: KLogger = KotlinLogging.logger {} private val timeoutLogger: KLogger = KotlinLogging.logger(logger.name + ".timeout") 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 fe18e24719..c7dd9223fd 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 @@ -49,6 +49,7 @@ 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.ApplicationContext +import org.utbot.framework.context.SimpleApplicationContext import org.utbot.framework.context.SpringApplicationContext import org.utbot.framework.plugin.api.* import org.utbot.framework.plugin.api.SpringSettings.* @@ -262,6 +263,7 @@ object UtTestsDialogProcessor { process.terminateOnException { _ -> val classpathForClassLoader = buildDirs + classpathList process.setupUtContext(classpathForClassLoader) + val simpleApplicationContext = SimpleApplicationContext(mockFrameworkInstalled, staticMockingConfigured) val applicationContext = when (model.projectType) { Spring -> { val beanDefinitions = @@ -281,15 +283,14 @@ object UtTestsDialogProcessor { clarifyBeanDefinitionReturnTypes(beanDefinitions, project) SpringApplicationContext( - mockFrameworkInstalled, - staticMockingConfigured, + simpleApplicationContext, clarifiedBeanDefinitions, shouldUseImplementors, model.springTestType, model.springSettings, ) } - else -> ApplicationContext(mockFrameworkInstalled, staticMockingConfigured) + else -> simpleApplicationContext } process.createTestGenerator( buildDirs, 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 102e5adeee..4eed08c35a 100644 --- a/utbot-testing/src/main/kotlin/org/utbot/testing/TestCodeGeneratorPipeline.kt +++ b/utbot-testing/src/main/kotlin/org/utbot/testing/TestCodeGeneratorPipeline.kt @@ -16,6 +16,7 @@ import org.utbot.framework.codegen.generator.SpringCodeGenerator 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.SimpleApplicationContext import org.utbot.framework.context.SpringApplicationContext import org.utbot.framework.plugin.api.* import org.utbot.framework.plugin.api.util.UtContext @@ -269,8 +270,7 @@ class TestCodeGeneratorPipeline(private val testInfrastructureConfiguration: Tes runtimeExceptionTestsBehaviour = runtimeExceptionTestsBehaviour, enableTestsTimeout = enableTestsTimeout, springCodeGenerationContext = SpringApplicationContext( - mockInstalled = true, - staticsMockingIsConfigured = true, + SimpleApplicationContext(mockFrameworkInstalled = true, staticsMockingIsConfigured = true), shouldUseImplementors = false, springTestType = SpringTestType.UNIT_TEST, springSettings = SpringSettings.AbsentSpringSettings(), From 5f9233bf25ebfc91ca62032bcf3856b9fc849dea Mon Sep 17 00:00:00 2001 From: IlyaMuravjov Date: Thu, 13 Jul 2023 11:53:14 +0300 Subject: [PATCH 04/10] Extract `MockerContext` interface --- .../src/main/kotlin/org/utbot/engine/Mocks.kt | 8 +++--- .../org/utbot/engine/UtBotSymbolicEngine.kt | 2 +- .../framework/context/ApplicationContext.kt | 4 +-- .../context/SimpleApplicationContext.kt | 26 ++----------------- .../framework/context/mocker/MockerContext.kt | 13 ++++++++++ .../context/mocker/SimpleMockerContext.kt | 14 ++++++++++ .../generator/UtTestsDialogProcessor.kt | 5 +++- .../testing/TestCodeGeneratorPipeline.kt | 8 +++++- 8 files changed, 47 insertions(+), 33 deletions(-) create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/context/mocker/MockerContext.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/context/mocker/SimpleMockerContext.kt 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 bf81618295..a6ff1cfba7 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.context.ApplicationContext +import org.utbot.framework.context.mocker.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/UtBotSymbolicEngine.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt index 56bfd7530b..e7de990da9 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt @@ -143,7 +143,7 @@ class UtBotSymbolicEngine( hierarchy, chosenClassesToMockAlways, MockListenerController(controller), - applicationContext = applicationContext, + mockerContext = applicationContext.mockerContext, ) fun attachMockListener(mockListener: MockListener) = mocker.mockListenerController?.attach(mockListener) 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 index 22dee50a0e..b23b0ccb2e 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/context/ApplicationContext.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/ApplicationContext.kt @@ -1,5 +1,6 @@ package org.utbot.framework.context +import org.utbot.framework.context.mocker.MockerContext import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.CodeGenerationContext import org.utbot.framework.plugin.api.TypeReplacementMode @@ -8,8 +9,7 @@ import soot.RefType import soot.SootField interface ApplicationContext : CodeGenerationContext { - val mockFrameworkInstalled: Boolean - val staticsMockingIsConfigured: Boolean + val mockerContext: MockerContext /** * Shows if there are any restrictions on type implementors. diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/context/SimpleApplicationContext.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/context/SimpleApplicationContext.kt index 623b95a3cb..00908b46f2 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/context/SimpleApplicationContext.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/SimpleApplicationContext.kt @@ -1,6 +1,7 @@ package org.utbot.framework.context import org.utbot.framework.UtSettings +import org.utbot.framework.context.mocker.MockerContext import org.utbot.framework.isFromTrustedLibrary import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.TypeReplacementMode @@ -10,33 +11,10 @@ import soot.SootField /** * 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 */ class SimpleApplicationContext( - override val mockFrameworkInstalled: Boolean = true, - staticsMockingIsConfigured: Boolean = true, + override val mockerContext: MockerContext ) : ApplicationContext { - override 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 - } - } - override val typeReplacementMode: TypeReplacementMode = TypeReplacementMode.AnyImplementor override fun replaceTypeIfNeeded(type: RefType): ClassId? = null diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/context/mocker/MockerContext.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/context/mocker/MockerContext.kt new file mode 100644 index 0000000000..ab5defefb8 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/mocker/MockerContext.kt @@ -0,0 +1,13 @@ +package org.utbot.framework.context.mocker + +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/mocker/SimpleMockerContext.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/context/mocker/SimpleMockerContext.kt new file mode 100644 index 0000000000..0e9a8b6f27 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/mocker/SimpleMockerContext.kt @@ -0,0 +1,14 @@ +package org.utbot.framework.context.mocker + +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-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 c7dd9223fd..98f7fca692 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 @@ -51,6 +51,7 @@ import org.utbot.framework.codegen.domain.ProjectType.* import org.utbot.framework.context.ApplicationContext import org.utbot.framework.context.SimpleApplicationContext import org.utbot.framework.context.SpringApplicationContext +import org.utbot.framework.context.mocker.SimpleMockerContext import org.utbot.framework.plugin.api.* import org.utbot.framework.plugin.api.SpringSettings.* import org.utbot.framework.plugin.api.SpringConfiguration.* @@ -263,7 +264,9 @@ object UtTestsDialogProcessor { process.terminateOnException { _ -> val classpathForClassLoader = buildDirs + classpathList process.setupUtContext(classpathForClassLoader) - val simpleApplicationContext = SimpleApplicationContext(mockFrameworkInstalled, staticMockingConfigured) + val simpleApplicationContext = SimpleApplicationContext( + SimpleMockerContext(mockFrameworkInstalled, staticMockingConfigured) + ) val applicationContext = when (model.projectType) { Spring -> { val beanDefinitions = 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 4eed08c35a..20b8a472e6 100644 --- a/utbot-testing/src/main/kotlin/org/utbot/testing/TestCodeGeneratorPipeline.kt +++ b/utbot-testing/src/main/kotlin/org/utbot/testing/TestCodeGeneratorPipeline.kt @@ -18,6 +18,7 @@ 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.SimpleApplicationContext import org.utbot.framework.context.SpringApplicationContext +import org.utbot.framework.context.mocker.SimpleMockerContext import org.utbot.framework.plugin.api.* import org.utbot.framework.plugin.api.util.UtContext import org.utbot.framework.plugin.api.util.description @@ -270,7 +271,12 @@ class TestCodeGeneratorPipeline(private val testInfrastructureConfiguration: Tes runtimeExceptionTestsBehaviour = runtimeExceptionTestsBehaviour, enableTestsTimeout = enableTestsTimeout, springCodeGenerationContext = SpringApplicationContext( - SimpleApplicationContext(mockFrameworkInstalled = true, staticsMockingIsConfigured = true), + SimpleApplicationContext( + SimpleMockerContext( + mockFrameworkInstalled = true, + staticsMockingIsConfigured = true + ) + ), shouldUseImplementors = false, springTestType = SpringTestType.UNIT_TEST, springSettings = SpringSettings.AbsentSpringSettings(), From 63a477f9fa085c21274d88d7a00359797bc97622 Mon Sep 17 00:00:00 2001 From: IlyaMuravjov Date: Thu, 13 Jul 2023 11:57:21 +0300 Subject: [PATCH 05/10] Configure default `ApplicationContext` for `TestCaseGenerator` --- .../org/utbot/framework/plugin/api/TestCaseGenerator.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) 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 bc9e9bdf45..b0082ff979 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 @@ -71,7 +71,12 @@ open class TestCaseGenerator( val engineActions: MutableList<(UtBotSymbolicEngine) -> Unit> = mutableListOf(), val isCanceled: () -> Boolean = { false }, val forceSootReload: Boolean = true, - val applicationContext: ApplicationContext = SimpleApplicationContext(), + val applicationContext: ApplicationContext = SimpleApplicationContext( + SimpleMockerContext( + mockFrameworkInstalled = true, + staticsMockingIsConfigured = true + ) + ), ) { private val logger: KLogger = KotlinLogging.logger {} private val timeoutLogger: KLogger = KotlinLogging.logger(logger.name + ".timeout") From 8d9930f2fd12fe836c1ede1b99ef4c3a9de2bfd4 Mon Sep 17 00:00:00 2001 From: IlyaMuravjov Date: Thu, 13 Jul 2023 12:13:44 +0300 Subject: [PATCH 06/10] Group contexts in packages by their kind (simple and Spring) --- utbot-framework/src/main/kotlin/org/utbot/engine/Mocks.kt | 2 +- .../main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt | 2 +- .../org/utbot/framework/context/ApplicationContext.kt | 1 - .../utbot/framework/context/{mocker => }/MockerContext.kt | 2 +- .../context/{ => simple}/SimpleApplicationContext.kt | 5 +++-- .../context/{mocker => simple}/SimpleMockerContext.kt | 4 +++- .../context/{ => spring}/SpringApplicationContext.kt | 3 ++- .../org/utbot/framework/plugin/api/TestCaseGenerator.kt | 5 +++-- .../intellij/plugin/generator/UtTestsDialogProcessor.kt | 7 +++---- .../kotlin/org/utbot/testing/TestCodeGeneratorPipeline.kt | 6 +++--- 10 files changed, 20 insertions(+), 17 deletions(-) rename utbot-framework/src/main/kotlin/org/utbot/framework/context/{mocker => }/MockerContext.kt (85%) rename utbot-framework/src/main/kotlin/org/utbot/framework/context/{ => simple}/SimpleApplicationContext.kt (87%) rename utbot-framework/src/main/kotlin/org/utbot/framework/context/{mocker => simple}/SimpleMockerContext.kt (83%) rename utbot-framework/src/main/kotlin/org/utbot/framework/context/{ => spring}/SpringApplicationContext.kt (98%) 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 a6ff1cfba7..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.context.mocker.MockerContext +import org.utbot.framework.context.MockerContext import org.utbot.framework.plugin.api.util.isInaccessibleViaReflection import soot.BooleanType import soot.RefType 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 e7de990da9..62ffc66da6 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt @@ -34,7 +34,7 @@ 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.SpringApplicationContext +import org.utbot.framework.context.spring.SpringApplicationContext import org.utbot.framework.plugin.api.* import org.utbot.framework.plugin.api.Step import org.utbot.framework.plugin.api.util.* 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 index b23b0ccb2e..821333f592 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/context/ApplicationContext.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/ApplicationContext.kt @@ -1,6 +1,5 @@ package org.utbot.framework.context -import org.utbot.framework.context.mocker.MockerContext import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.CodeGenerationContext import org.utbot.framework.plugin.api.TypeReplacementMode diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/context/mocker/MockerContext.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/context/MockerContext.kt similarity index 85% rename from utbot-framework/src/main/kotlin/org/utbot/framework/context/mocker/MockerContext.kt rename to utbot-framework/src/main/kotlin/org/utbot/framework/context/MockerContext.kt index ab5defefb8..a85f591f5d 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/context/mocker/MockerContext.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/MockerContext.kt @@ -1,4 +1,4 @@ -package org.utbot.framework.context.mocker +package org.utbot.framework.context interface MockerContext { /** diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/context/SimpleApplicationContext.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/context/simple/SimpleApplicationContext.kt similarity index 87% rename from utbot-framework/src/main/kotlin/org/utbot/framework/context/SimpleApplicationContext.kt rename to utbot-framework/src/main/kotlin/org/utbot/framework/context/simple/SimpleApplicationContext.kt index 00908b46f2..8348ea8d3f 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/context/SimpleApplicationContext.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/simple/SimpleApplicationContext.kt @@ -1,7 +1,8 @@ -package org.utbot.framework.context +package org.utbot.framework.context.simple import org.utbot.framework.UtSettings -import org.utbot.framework.context.mocker.MockerContext +import org.utbot.framework.context.ApplicationContext +import org.utbot.framework.context.MockerContext import org.utbot.framework.isFromTrustedLibrary import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.TypeReplacementMode diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/context/mocker/SimpleMockerContext.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/context/simple/SimpleMockerContext.kt similarity index 83% rename from utbot-framework/src/main/kotlin/org/utbot/framework/context/mocker/SimpleMockerContext.kt rename to utbot-framework/src/main/kotlin/org/utbot/framework/context/simple/SimpleMockerContext.kt index 0e9a8b6f27..e7b0977d9e 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/context/mocker/SimpleMockerContext.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/simple/SimpleMockerContext.kt @@ -1,4 +1,6 @@ -package org.utbot.framework.context.mocker +package org.utbot.framework.context.simple + +import org.utbot.framework.context.MockerContext class SimpleMockerContext( override val mockFrameworkInstalled: Boolean, diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/context/SpringApplicationContext.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringApplicationContext.kt similarity index 98% rename from utbot-framework/src/main/kotlin/org/utbot/framework/context/SpringApplicationContext.kt rename to utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringApplicationContext.kt index 2b80dd52d6..df32770b24 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/context/SpringApplicationContext.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringApplicationContext.kt @@ -1,8 +1,9 @@ -package org.utbot.framework.context +package org.utbot.framework.context.spring import mu.KotlinLogging import org.utbot.common.isAbstract import org.utbot.common.isStatic +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.SpringCodeGenerationContext 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 b0082ff979..2af6cda9c0 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 @@ -26,8 +26,9 @@ 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.SimpleApplicationContext -import org.utbot.framework.context.SpringApplicationContext +import org.utbot.framework.context.simple.SimpleApplicationContext +import org.utbot.framework.context.simple.SimpleMockerContext +import org.utbot.framework.context.spring.SpringApplicationContext import org.utbot.framework.plugin.api.utils.checkFrameworkDependencies import org.utbot.framework.minimization.minimizeTestCase import org.utbot.framework.plugin.api.util.SpringModelUtils.entityClassIds 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 98f7fca692..c8038a3c99 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,10 +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.ApplicationContext -import org.utbot.framework.context.SimpleApplicationContext -import org.utbot.framework.context.SpringApplicationContext -import org.utbot.framework.context.mocker.SimpleMockerContext +import org.utbot.framework.context.simple.SimpleApplicationContext +import org.utbot.framework.context.spring.SpringApplicationContext +import org.utbot.framework.context.simple.SimpleMockerContext import org.utbot.framework.plugin.api.* import org.utbot.framework.plugin.api.SpringSettings.* import org.utbot.framework.plugin.api.SpringConfiguration.* 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 20b8a472e6..088cabe399 100644 --- a/utbot-testing/src/main/kotlin/org/utbot/testing/TestCodeGeneratorPipeline.kt +++ b/utbot-testing/src/main/kotlin/org/utbot/testing/TestCodeGeneratorPipeline.kt @@ -16,9 +16,9 @@ import org.utbot.framework.codegen.generator.SpringCodeGenerator 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.SimpleApplicationContext -import org.utbot.framework.context.SpringApplicationContext -import org.utbot.framework.context.mocker.SimpleMockerContext +import org.utbot.framework.context.simple.SimpleApplicationContext +import org.utbot.framework.context.spring.SpringApplicationContext +import org.utbot.framework.context.simple.SimpleMockerContext import org.utbot.framework.plugin.api.* import org.utbot.framework.plugin.api.util.UtContext import org.utbot.framework.plugin.api.util.description From a2c969cfe68c7bcc18f2291919ec755c3a793975 Mon Sep 17 00:00:00 2001 From: IlyaMuravjov Date: Thu, 13 Jul 2023 14:25:31 +0300 Subject: [PATCH 07/10] Extract `TypeReplacer` and `NonNullSpeculator`, replace `shouldUseImplementors` with `beanDefinitions.isNotEmpty()` --- .../main/kotlin/org/utbot/engine/Traverser.kt | 13 +- .../org/utbot/engine/UtBotSymbolicEngine.kt | 3 +- .../framework/context/ApplicationContext.kt | 28 +--- .../framework/context/NonNullSpeculator.kt | 17 ++ .../utbot/framework/context/TypeReplacer.kt | 18 +++ .../simple/SimpleApplicationContext.kt | 23 +-- .../context/simple/SimpleNonNullSpeculator.kt | 17 ++ .../context/simple/SimpleTypeReplacer.kt | 12 ++ .../spring/SpringApplicationContext.kt | 148 +----------------- .../spring/SpringApplicationContextImpl.kt | 102 ++++++++++++ .../context/spring/SpringNonNullSpeculator.kt | 19 +++ .../context/spring/SpringTypeReplacer.kt | 26 +++ .../generator/UtTestsDialogProcessor.kt | 7 +- .../testing/TestCodeGeneratorPipeline.kt | 5 +- 14 files changed, 240 insertions(+), 198 deletions(-) create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/context/NonNullSpeculator.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/context/TypeReplacer.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/context/simple/SimpleNonNullSpeculator.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/context/simple/SimpleTypeReplacer.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringApplicationContextImpl.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringNonNullSpeculator.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringTypeReplacer.kt 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 91f2efe7c4..38c7e71d65 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt @@ -117,6 +117,8 @@ import org.utbot.framework.UtSettings.preferredCexOption import org.utbot.framework.UtSettings.substituteStaticsWithSymbolicVariable import org.utbot.framework.isFromTrustedLibrary 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,7 +2339,7 @@ class Traverser( * See more detailed documentation in [ApplicationContext] mentioned methods. */ private fun checkAndMarkLibraryFieldSpeculativelyNotNull(field: SootField, createdField: SymbolicValue) { - if (applicationContext.speculativelyCannotProduceNullPointerException(field, methodUnderTest.classId)) + if (nonNullSpeculator.speculativelyCannotProduceNullPointerException(field, methodUnderTest.classId)) markAsSpeculativelyNotNull(createdField.addr) } 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 62ffc66da6..fa25d5fa14 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt @@ -179,7 +179,8 @@ class UtBotSymbolicEngine( typeResolver, globalGraph, mocker, - applicationContext, + applicationContext.typeReplacer, + applicationContext.nonNullSpeculator, taintContext, ) 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 index 821333f592..42b3b97450 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/context/ApplicationContext.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/ApplicationContext.kt @@ -1,36 +1,12 @@ package org.utbot.framework.context -import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.CodeGenerationContext -import org.utbot.framework.plugin.api.TypeReplacementMode import org.utbot.framework.plugin.api.UtError -import soot.RefType -import soot.SootField interface ApplicationContext : CodeGenerationContext { val mockerContext: MockerContext - - /** - * 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? - - /** - * 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 + val typeReplacer: TypeReplacer + val nonNullSpeculator: NonNullSpeculator fun preventsFurtherTestGeneration(): Boolean 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/simple/SimpleApplicationContext.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/context/simple/SimpleApplicationContext.kt index 8348ea8d3f..b79b45876b 100644 --- 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 @@ -1,32 +1,19 @@ package org.utbot.framework.context.simple -import org.utbot.framework.UtSettings import org.utbot.framework.context.ApplicationContext import org.utbot.framework.context.MockerContext -import org.utbot.framework.isFromTrustedLibrary -import org.utbot.framework.plugin.api.ClassId -import org.utbot.framework.plugin.api.TypeReplacementMode +import org.utbot.framework.context.NonNullSpeculator +import org.utbot.framework.context.TypeReplacer import org.utbot.framework.plugin.api.UtError -import soot.RefType -import soot.SootField /** * A context to use when no specific data is required. */ class SimpleApplicationContext( - override val mockerContext: MockerContext + override val mockerContext: MockerContext, + override val typeReplacer: TypeReplacer = SimpleTypeReplacer(), + override val nonNullSpeculator: NonNullSpeculator = SimpleNonNullSpeculator() ) : ApplicationContext { - override val typeReplacementMode: TypeReplacementMode = TypeReplacementMode.AnyImplementor - - override fun replaceTypeIfNeeded(type: RefType): ClassId? = null - - override fun speculativelyCannotProduceNullPointerException( - field: SootField, - classUnderTest: ClassId, - ): Boolean = - !UtSettings.maximizeCoverageUsingReflection && - field.declaringClass.isFromTrustedLibrary() && - (field.isFinal || !field.isPublic) override fun preventsFurtherTestGeneration(): Boolean = false 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 index df32770b24..f04002a775 100644 --- 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 @@ -1,156 +1,24 @@ package org.utbot.framework.context.spring -import mu.KotlinLogging -import org.utbot.common.isAbstract -import org.utbot.common.isStatic 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.SpringCodeGenerationContext import org.utbot.framework.plugin.api.SpringContextLoadingResult -import org.utbot.framework.plugin.api.SpringSettings -import org.utbot.framework.plugin.api.SpringTestType -import org.utbot.framework.plugin.api.TypeReplacementMode -import org.utbot.framework.plugin.api.UtError -import org.utbot.framework.plugin.api.classId -import org.utbot.framework.plugin.api.id -import org.utbot.framework.plugin.api.isAbstractType -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.id -import org.utbot.framework.plugin.api.util.isSubtypeOf -import org.utbot.framework.plugin.api.util.jClass -import org.utbot.framework.plugin.api.util.utContext -import soot.RefType -import soot.SootField /** * 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( - private val delegateContext: ApplicationContext, - val beanDefinitions: List = emptyList(), - private val shouldUseImplementors: Boolean, - override val springTestType: SpringTestType, - override val springSettings: SpringSettings, -): ApplicationContext by delegateContext, SpringCodeGenerationContext { - - override var springContextLoadingResult: SpringContextLoadingResult? = null - - 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) TypeReplacementMode.KnownImplementor else TypeReplacementMode.NoImplementors - +// TODO #2358 +interface SpringApplicationContext : ApplicationContext, SpringCodeGenerationContext { /** - * Replaces an interface type with its implementor type - * if there is the unique implementor in bean definitions. + * Describes bean definitions (bean name, type, some optional additional data) */ - override fun replaceTypeIfNeeded(type: RefType): ClassId? = - if (type.isAbstractType) { - springInjectedClasses.singleOrNull { it.isSubtypeOf(type.id) } - } else { - null - } - - /** - * 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 = - delegateContext.preventsFurtherTestGeneration() || springContextLoadingResult?.contextLoaded == false - - override fun getErrors(): List = - springContextLoadingResult?.exceptions?.map { exception -> - UtError( - "Failed to load Spring application context", - exception - ) - }.orEmpty() + delegateContext.getErrors() + val beanDefinitions: List + val springInjectedClasses: Set + val allInjectedTypes: Set - 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 } - } + override var springContextLoadingResult: SpringContextLoadingResult? + 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..376cd58cee --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringApplicationContextImpl.kt @@ -0,0 +1,102 @@ +package org.utbot.framework.context.spring + +import mu.KotlinLogging +import org.utbot.common.isAbstract +import org.utbot.common.isStatic +import org.utbot.framework.context.ApplicationContext +import org.utbot.framework.context.NonNullSpeculator +import org.utbot.framework.context.TypeReplacer +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.SpringSettings +import org.utbot.framework.plugin.api.SpringTestType +import org.utbot.framework.plugin.api.UtError +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(), + override 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 springContextLoadingResult: SpringContextLoadingResult? = null + + override fun preventsFurtherTestGeneration(): Boolean = + delegateContext.preventsFurtherTestGeneration() || springContextLoadingResult?.contextLoaded == false + + override fun getErrors(): List = + springContextLoadingResult?.exceptions?.map { exception -> + UtError( + "Failed to load Spring application context", + exception + ) + }.orEmpty() + delegateContext.getErrors() + + 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 springInjectedClasses: 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 allInjectedTypes: Set + get() { + if (!areInjectedTypesInitialized) { + _allInjectedSuperTypes = springInjectedClasses.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/SpringNonNullSpeculator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringNonNullSpeculator.kt new file mode 100644 index 0000000000..d286dfdafe --- /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.allInjectedTypes + +} \ 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..9b476d95eb --- /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.springInjectedClasses.singleOrNull { it.isSubtypeOf(type.id) } + else null +} \ No newline at end of file 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 c8038a3c99..16d9874643 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 @@ -49,8 +49,8 @@ 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.spring.SpringApplicationContext 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.* @@ -279,15 +279,12 @@ object UtTestsDialogProcessor { } } - val shouldUseImplementors = beanDefinitions.isNotEmpty() - val clarifiedBeanDefinitions = clarifyBeanDefinitionReturnTypes(beanDefinitions, project) - SpringApplicationContext( + SpringApplicationContextImpl( simpleApplicationContext, clarifiedBeanDefinitions, - shouldUseImplementors, model.springTestType, model.springSettings, ) 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 088cabe399..a26c9a6f52 100644 --- a/utbot-testing/src/main/kotlin/org/utbot/testing/TestCodeGeneratorPipeline.kt +++ b/utbot-testing/src/main/kotlin/org/utbot/testing/TestCodeGeneratorPipeline.kt @@ -17,8 +17,8 @@ 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.simple.SimpleApplicationContext -import org.utbot.framework.context.spring.SpringApplicationContext 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.util.UtContext import org.utbot.framework.plugin.api.util.description @@ -270,14 +270,13 @@ class TestCodeGeneratorPipeline(private val testInfrastructureConfiguration: Tes parameterizedTestSource = parametrizedTestSource, runtimeExceptionTestsBehaviour = runtimeExceptionTestsBehaviour, enableTestsTimeout = enableTestsTimeout, - springCodeGenerationContext = SpringApplicationContext( + springCodeGenerationContext = SpringApplicationContextImpl( SimpleApplicationContext( SimpleMockerContext( mockFrameworkInstalled = true, staticsMockingIsConfigured = true ) ), - shouldUseImplementors = false, springTestType = SpringTestType.UNIT_TEST, springSettings = SpringSettings.AbsentSpringSettings(), ) From b801da653c52f621293fed819efe43a020d5c23e Mon Sep 17 00:00:00 2001 From: IlyaMuravjov Date: Thu, 13 Jul 2023 14:49:13 +0300 Subject: [PATCH 08/10] Extract `ConcreteExecutionContext` --- .../framework/context/ApplicationContext.kt | 8 +++---- .../context/ConcreteExecutionContext.kt | 15 +++++++++++++ .../simple/SimpleApplicationContext.kt | 10 ++++----- .../simple/SimpleConcreteExecutionContext.kt | 14 +++++++++++++ .../spring/SpringApplicationContextImpl.kt | 18 +++++++--------- .../spring/SpringConcreteExecutionContext.kt | 21 +++++++++++++++++++ .../framework/plugin/api/TestCaseGenerator.kt | 13 ++++++++---- 7 files changed, 76 insertions(+), 23 deletions(-) create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/context/ConcreteExecutionContext.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/context/simple/SimpleConcreteExecutionContext.kt create mode 100644 utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringConcreteExecutionContext.kt 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 index 42b3b97450..51ce384a20 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/context/ApplicationContext.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/ApplicationContext.kt @@ -1,14 +1,14 @@ package org.utbot.framework.context import org.utbot.framework.plugin.api.CodeGenerationContext -import org.utbot.framework.plugin.api.UtError interface ApplicationContext : CodeGenerationContext { val mockerContext: MockerContext val typeReplacer: TypeReplacer val nonNullSpeculator: NonNullSpeculator - fun preventsFurtherTestGeneration(): Boolean - - fun getErrors(): List + fun createConcreteExecutionContext( + fullClasspath: String, + classpathWithoutDependencies: String + ): ConcreteExecutionContext } \ 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..f5d0804435 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/ConcreteExecutionContext.kt @@ -0,0 +1,15 @@ +package org.utbot.framework.context + +import org.utbot.framework.plugin.api.UtError + +interface ConcreteExecutionContext { + fun preventsFurtherTestGeneration(): Boolean + + fun getErrors(): List + + // TODO refactor, so this interface only includes the following: + // val instrumentation: Instrumentation + // fun createValueProviderOrThrow(classUnderTest: ClassId, idGenerator: IdentityPreservingIdGenerator): JavaValueProvider + // fun loadContext(): ContextLoadingResult + // fun Coverage.filterCoveredInstructions(classUnderTestId: ClassId): Coverage +} \ 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 index b79b45876b..3b35ddca46 100644 --- 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 @@ -1,10 +1,10 @@ package org.utbot.framework.context.simple 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 -import org.utbot.framework.plugin.api.UtError /** * A context to use when no specific data is required. @@ -14,8 +14,8 @@ class SimpleApplicationContext( override val typeReplacer: TypeReplacer = SimpleTypeReplacer(), override val nonNullSpeculator: NonNullSpeculator = SimpleNonNullSpeculator() ) : ApplicationContext { - - override fun preventsFurtherTestGeneration(): Boolean = false - - override fun getErrors(): List = emptyList() + override fun createConcreteExecutionContext( + fullClasspath: String, + classpathWithoutDependencies: String + ): ConcreteExecutionContext = SimpleConcreteExecutionContext(fullClasspath, classpathWithoutDependencies) } \ 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..dc910bb3fc --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/simple/SimpleConcreteExecutionContext.kt @@ -0,0 +1,14 @@ +package org.utbot.framework.context.simple + +import org.utbot.framework.context.ConcreteExecutionContext +import org.utbot.framework.plugin.api.UtError + +class SimpleConcreteExecutionContext( + // TODO these properties will be used later (to fulfill TODO in ConcreteExecutionContext) + val fullClassPath: String, + val classpathWithoutDependencies: String +) : ConcreteExecutionContext { + override fun preventsFurtherTestGeneration(): Boolean = false + + override fun getErrors(): List = emptyList() +} \ 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 index 376cd58cee..cd27f602c4 100644 --- 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 @@ -4,6 +4,7 @@ import mu.KotlinLogging import org.utbot.common.isAbstract import org.utbot.common.isStatic 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.plugin.api.BeanDefinitionData @@ -32,16 +33,13 @@ class SpringApplicationContextImpl( override var springContextLoadingResult: SpringContextLoadingResult? = null - override fun preventsFurtherTestGeneration(): Boolean = - delegateContext.preventsFurtherTestGeneration() || springContextLoadingResult?.contextLoaded == false - - override fun getErrors(): List = - springContextLoadingResult?.exceptions?.map { exception -> - UtError( - "Failed to load Spring application context", - exception - ) - }.orEmpty() + delegateContext.getErrors() + override fun createConcreteExecutionContext( + fullClasspath: String, + classpathWithoutDependencies: String + ): ConcreteExecutionContext = SpringConcreteExecutionContext( + delegateContext.createConcreteExecutionContext(fullClasspath, classpathWithoutDependencies), + this + ) override fun getBeansAssignableTo(classId: ClassId): List = beanDefinitions.filter { beanDef -> // some bean classes may fail to load diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringConcreteExecutionContext.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringConcreteExecutionContext.kt new file mode 100644 index 0000000000..5d1a87f565 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringConcreteExecutionContext.kt @@ -0,0 +1,21 @@ +package org.utbot.framework.context.spring + +import org.utbot.framework.context.ConcreteExecutionContext +import org.utbot.framework.plugin.api.UtError + +class SpringConcreteExecutionContext( + private val delegateContext: ConcreteExecutionContext, + private val springApplicationContext: SpringApplicationContext, +) : ConcreteExecutionContext { + override fun preventsFurtherTestGeneration(): Boolean = + delegateContext.preventsFurtherTestGeneration() || + springApplicationContext.springContextLoadingResult?.contextLoaded == false + + override fun getErrors(): List = + springApplicationContext.springContextLoadingResult?.exceptions?.map { exception -> + UtError( + "Failed to load Spring application context", + exception + ) + }.orEmpty() + delegateContext.getErrors() +} \ No newline at end of file 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 2af6cda9c0..8eb641bf60 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 @@ -81,6 +81,11 @@ open class TestCaseGenerator( ) { private val logger: KLogger = KotlinLogging.logger {} private val timeoutLogger: KLogger = KotlinLogging.logger(logger.name + ".timeout") + private val concreteExecutionContext = applicationContext.createConcreteExecutionContext( + fullClasspath = classpathForEngine, + classpathWithoutDependencies = buildDirs.joinToString(File.pathSeparator) + ) + private val executionInstrumentation by lazy { when (applicationContext) { is SpringApplicationContext -> when (val settings = applicationContext.springSettings) { @@ -158,8 +163,8 @@ open class TestCaseGenerator( return@flow doContextDependentPreparationForTestGeneration() - applicationContext.getErrors().forEach { emit(it) } - if (applicationContext.preventsFurtherTestGeneration()) + concreteExecutionContext.getErrors().forEach { emit(it) } + if (concreteExecutionContext.preventsFurtherTestGeneration()) return@flow try { @@ -194,10 +199,10 @@ open class TestCaseGenerator( doContextDependentPreparationForTestGeneration() val method2errors: Map> = methods.associateWith { - applicationContext.getErrors().associateTo(mutableMapOf()) { it.description to 1 } + concreteExecutionContext.getErrors().associateTo(mutableMapOf()) { it.description to 1 } } - if (applicationContext.preventsFurtherTestGeneration()) + if (concreteExecutionContext.preventsFurtherTestGeneration()) return@use methods.map { method -> UtMethodTestSet(method, errors = method2errors.getValue(method)) } val executionStartInMillis = System.currentTimeMillis() From ccd3f201e9b39a47403db54538fb81d0114f2058 Mon Sep 17 00:00:00 2001 From: IlyaMuravjov Date: Thu, 13 Jul 2023 16:56:43 +0300 Subject: [PATCH 09/10] Make naming of `SpringApplicationContext` properties more consistent --- .../framework/context/spring/SpringApplicationContext.kt | 4 ++-- .../context/spring/SpringApplicationContextImpl.kt | 7 +++---- .../framework/context/spring/SpringNonNullSpeculator.kt | 2 +- .../utbot/framework/context/spring/SpringTypeReplacer.kt | 2 +- 4 files changed, 7 insertions(+), 8 deletions(-) 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 index f04002a775..15fab49219 100644 --- 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 @@ -16,8 +16,8 @@ interface SpringApplicationContext : ApplicationContext, SpringCodeGenerationCon * Describes bean definitions (bean name, type, some optional additional data) */ val beanDefinitions: List - val springInjectedClasses: Set - val allInjectedTypes: Set + val injectedTypes: Set + val allInjectedSuperTypes: Set override var springContextLoadingResult: SpringContextLoadingResult? fun getBeansAssignableTo(classId: ClassId): List 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 index cd27f602c4..790e9320c2 100644 --- 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 @@ -12,7 +12,6 @@ import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.SpringContextLoadingResult import org.utbot.framework.plugin.api.SpringSettings import org.utbot.framework.plugin.api.SpringTestType -import org.utbot.framework.plugin.api.UtError import org.utbot.framework.plugin.api.util.allSuperTypes import org.utbot.framework.plugin.api.util.id import org.utbot.framework.plugin.api.util.jClass @@ -50,7 +49,7 @@ class SpringApplicationContextImpl( } // Classes representing concrete types that are actually used in Spring application - override val springInjectedClasses: Set + override val injectedTypes: Set get() { if (!areAllInjectedSuperTypesInitialized) { for (beanTypeName in beanDefinitions.map { it.beanTypeName }) { @@ -80,10 +79,10 @@ class SpringApplicationContextImpl( return _injectedTypes } - override val allInjectedTypes: Set + override val allInjectedSuperTypes: Set get() { if (!areInjectedTypesInitialized) { - _allInjectedSuperTypes = springInjectedClasses.flatMap { it.allSuperTypes() }.toSet() + _allInjectedSuperTypes = injectedTypes.flatMap { it.allSuperTypes() }.toSet() areInjectedTypesInitialized = true } 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 index d286dfdafe..5f49d7800d 100644 --- 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 @@ -14,6 +14,6 @@ class SpringNonNullSpeculator( 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.allInjectedTypes + 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 index 9b476d95eb..78362c7520 100644 --- 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 @@ -21,6 +21,6 @@ class SpringTypeReplacer( 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.springInjectedClasses.singleOrNull { it.isSubtypeOf(type.id) } + if (type.isAbstractType) springApplicationContext.injectedTypes.singleOrNull { it.isSubtypeOf(type.id) } else null } \ No newline at end of file From 656962858ab8180437930420c46aaa507dc514a9 Mon Sep 17 00:00:00 2001 From: IlyaMuravjov Date: Mon, 17 Jul 2023 10:58:16 +0300 Subject: [PATCH 10/10] Pass `Instrumentation.Factory` via Kryo instead of `Instrumentation` itself --- .../org/utbot/engine/UtBotSymbolicEngine.kt | 6 +- .../org/utbot/external/api/UtBotJavaApi.kt | 4 +- .../context/ConcreteExecutionContext.kt | 2 +- .../framework/coverage/CoverageCalculator.kt | 2 +- .../framework/plugin/api/TestCaseGenerator.kt | 37 ++++---- .../org/utbot/examples/TestConstructors.kt | 10 +-- .../examples/TestCoverageInstrumentation.kt | 16 ++-- .../examples/TestInvokeInstrumentation.kt | 14 +-- .../TestInvokeWithStaticsInstrumentation.kt | 8 +- .../kotlin/org/utbot/examples/TestIsolated.kt | 12 +-- .../org/utbot/examples/TestStaticMethods.kt | 6 +- .../utbot/examples/TestWithInstrumentation.kt | 6 +- .../org/utbot/examples/benchmark/Benchmark.kt | 4 +- .../examples/benchmark/BenchmarkFibonacci.kt | 4 +- .../benchmark/TestBenchmarkClasses.kt | 4 +- .../org/utbot/examples/et/TestMixedExTrace.kt | 2 +- .../utbot/examples/et/TestSimpleExTrace.kt | 12 +-- .../org/utbot/examples/et/TestStaticsUsage.kt | 4 +- .../utbot/examples/jacoco/TestSameAsJaCoCo.kt | 2 +- .../utbot/instrumentation/ConcreteExecutor.kt | 30 +++---- .../org/utbot/instrumentation/Executor.kt | 2 +- .../instrumentation/Instrumentation.kt | 11 ++- .../instrumentation/InvokeInstrumentation.kt | 4 + .../InvokeWithStaticsInstrumentation.kt | 4 + .../coverage/CoverageInstrumentation.kt | 6 +- .../et/ExecutionTraceInstrumentation.kt | 4 + .../execution/UtExecutionInstrumentation.kt | 56 ++++++++---- .../SpringUtExecutionInstrumentation.kt | 87 +++++++++++-------- .../process/InstrumentedProcessMain.kt | 5 +- .../instrumentation/rd/InstrumentedProcess.kt | 4 +- 30 files changed, 213 insertions(+), 155 deletions(-) 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 fa25d5fa14..0cebe6c6ad 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt @@ -54,6 +54,7 @@ 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 +import org.utbot.instrumentation.instrumentation.execution.UtExecutionInstrumentation import org.utbot.taint.* import org.utbot.taint.model.TaintConfiguration import soot.jimple.Stmt @@ -117,10 +118,11 @@ class UtBotSymbolicEngine( val mockStrategy: MockStrategy = NO_MOCKS, chosenClassesToMockAlways: Set, val applicationContext: ApplicationContext, - executionInstrumentation: Instrumentation, + executionInstrumentationFactory: UtExecutionInstrumentation.Factory<*>, userTaintConfigurationProvider: TaintConfigurationProvider? = null, private val solverTimeoutInMillis: Int = checkSolverTimeoutMillis, ) : UtContextInitializer() { + private val graph = methodUnderTest.sootMethod.jimpleBody().apply { logger.trace { "JIMPLE for $methodUnderTest:\n$this" } }.graph() @@ -189,7 +191,7 @@ class UtBotSymbolicEngine( private val concreteExecutor = ConcreteExecutor( - executionInstrumentation, + executionInstrumentationFactory, classpath, ).apply { this.classLoader = utContext.classLoader } 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..89b778fa2c 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 @@ -40,6 +40,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 +73,7 @@ object UtBotJavaApi { val testSets: MutableList = generatedTestCases.toMutableList() val concreteExecutor = ConcreteExecutor( - UtExecutionInstrumentation, + SimpleUtExecutionInstrumentation.Factory(pathsToUserClasses = classpath.split(File.pathSeparator).toSet()), classpath, ) 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 index f5d0804435..ec04d58132 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/context/ConcreteExecutionContext.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/ConcreteExecutionContext.kt @@ -8,7 +8,7 @@ interface ConcreteExecutionContext { fun getErrors(): List // TODO refactor, so this interface only includes the following: - // val instrumentation: Instrumentation + // val instrumentationFactory: UtExecutionInstrumentation.Factory<*> // fun createValueProviderOrThrow(classUnderTest: ClassId, idGenerator: IdentityPreservingIdGenerator): JavaValueProvider // fun loadContext(): ContextLoadingResult // fun Coverage.filterCoveredInstructions(classUnderTestId: ClassId): Coverage 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..8c29eadb2f 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 8eb641bf60..030e7736fa 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 @@ -43,8 +43,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.SimpleUtExecutionInstrumentation import org.utbot.instrumentation.instrumentation.execution.UtExecutionInstrumentation +import org.utbot.instrumentation.instrumentation.spring.SpringUtExecutionInstrumentation import org.utbot.instrumentation.tryLoadingSpringContext import org.utbot.instrumentation.warmup import org.utbot.taint.TaintConfigurationProvider @@ -86,25 +87,29 @@ open class TestCaseGenerator( classpathWithoutDependencies = buildDirs.joinToString(File.pathSeparator) ) - private val executionInstrumentation by lazy { + private val executionInstrumentationFactory: UtExecutionInstrumentation.Factory<*> = run { + val simpleUtExecutionInstrumentationFactory = SimpleUtExecutionInstrumentation.Factory(classpathForEngine.split(File.pathSeparator).toSet()) 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(), - ) + is SpringApplicationContext -> { + when (val settings = applicationContext.springSettings) { + is AbsentSpringSettings -> simpleUtExecutionInstrumentationFactory + is PresentSpringSettings -> when (applicationContext.springTestType) { + UNIT_TEST -> simpleUtExecutionInstrumentationFactory + INTEGRATION_TEST -> SpringUtExecutionInstrumentation.Factory( + simpleUtExecutionInstrumentationFactory, + settings, + applicationContext.beanDefinitions, + buildDirs.map { it.toURL() }.toTypedArray(), + ) + } } } - else -> UtExecutionInstrumentation + else -> simpleUtExecutionInstrumentationFactory } } + private val classpathForEngine: String get() = (buildDirs + listOfNotNull(classpath)).joinToString(File.pathSeparator) @@ -128,7 +133,7 @@ open class TestCaseGenerator( // force pool to create an appropriate executor // TODO ensure that instrumented process that starts here is properly terminated ConcreteExecutor( - executionInstrumentation, + executionInstrumentationFactory, classpathForEngine, ).apply { warmup() @@ -335,7 +340,7 @@ open class TestCaseGenerator( mockStrategy = mockStrategyApi.toModel(), chosenClassesToMockAlways = chosenClassesToMockAlways, applicationContext = applicationContext, - executionInstrumentation = executionInstrumentation, + executionInstrumentationFactory = executionInstrumentationFactory, solverTimeoutInMillis = executionTimeEstimator.updatedSolverCheckTimeoutMillis, userTaintConfigurationProvider = userTaintConfigurationProvider, ) @@ -471,7 +476,7 @@ open class TestCaseGenerator( if (applicationContext.springContextLoadingResult == null) // force pool to create an appropriate executor applicationContext.springContextLoadingResult = ConcreteExecutor( - executionInstrumentation, + executionInstrumentationFactory, classpathForEngine ).tryLoadingSpringContext() } 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..7d37d0c569 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..de520914c9 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..1bb05465e8 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..cda707c17a 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..3538e2979b 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..bc33834f80 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..22e6308c52 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..55da2d91df 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..e65de088a2 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..c660d9291a 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..cedaf15539 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..dbb732eb7c 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..1375cc898f 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..88187068d1 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..ddbbe76a71 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/ConcreteExecutor.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/ConcreteExecutor.kt @@ -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 ) 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..73e11d7114 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 + + class 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..5bd1253128 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]) } } } } + + class 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..98fbcb0e37 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 } + + class 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..9424d9f8d9 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 } } + + class 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/UtExecutionInstrumentation.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/UtExecutionInstrumentation.kt index 824393c1bb..c7a7185f9c 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 @@ -47,20 +47,39 @@ data class UtConcreteExecutionResult( } } -// TODO if possible make it non singleton -object UtExecutionInstrumentation : Instrumentation { - private val delegateInstrumentation = InvokeInstrumentation() +interface UtExecutionInstrumentation : Instrumentation { + override fun invoke( + clazz: Class<*>, + methodSignature: String, + arguments: ArgumentList, + parameters: Any? + ): UtConcreteExecutionResult = invoke( + clazz, methodSignature, arguments, parameters, phasesWrapper = { invokeBasePhases -> invokeBasePhases() } + ) - var instrumentationContext: InstrumentationContext = SimpleInstrumentationContext() + fun invoke( + clazz: Class<*>, + methodSignature: String, + arguments: ArgumentList, + parameters: Any?, + phasesWrapper: PhasesController.(invokeBasePhases: () -> UtConcreteExecutionResult) -> UtConcreteExecutionResult + ): UtConcreteExecutionResult - private val traceHandler = TraceHandler() - private val ndDetector = NonDeterministicDetector() - private val pathsToUserClasses = mutableSetOf() + interface Factory : Instrumentation.Factory { + override fun create(): TInstrumentation = create(SimpleInstrumentationContext()) - override fun init(pathsToUserClasses: Set) { - UtExecutionInstrumentation.pathsToUserClasses.clear() - UtExecutionInstrumentation.pathsToUserClasses += pathsToUserClasses + fun create(instrumentationContext: InstrumentationContext): TInstrumentation } +} + +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 @@ -69,14 +88,6 @@ object UtExecutionInstrumentation : Instrumentation { * Argument [parameters] must be of type [UtConcreteExecutionData]. */ override fun invoke( - clazz: Class<*>, - methodSignature: String, - arguments: ArgumentList, - parameters: Any? - ): UtConcreteExecutionResult = - invoke(clazz, methodSignature, arguments, parameters, phasesWrapper = { it() }) - - fun invoke( clazz: Class<*>, methodSignature: String, arguments: ArgumentList, @@ -191,4 +202,13 @@ object UtExecutionInstrumentation : Instrumentation { 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) + } } 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..64cc38c6fe 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.FieldId import org.utbot.framework.plugin.api.SpringContextLoadingResult 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,23 +48,6 @@ 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 { val apiProviderResult = instrumentationContext.springApiProviderResult return SpringContextLoadingResult( @@ -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,27 @@ 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 + ) + } } \ 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")