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..9faf511ab6 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Mocks.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Mocks.kt @@ -19,7 +19,7 @@ import kotlinx.collections.immutable.persistentListOf import org.utbot.common.nameOfPackage import org.utbot.engine.types.OBJECT_TYPE import org.utbot.engine.util.mockListeners.MockListenerController -import org.utbot.framework.plugin.api.ApplicationContext +import org.utbot.framework.context.MockerContext import org.utbot.framework.plugin.api.util.isInaccessibleViaReflection import soot.BooleanType import soot.RefType @@ -168,7 +168,7 @@ class Mocker( private val hierarchy: Hierarchy, chosenClassesToMockAlways: Set, internal val mockListenerController: MockListenerController? = null, - private val applicationContext: ApplicationContext, + private val mockerContext: MockerContext, ) { private val mocksAreDesired: Boolean = strategy != MockStrategy.NO_MOCKS @@ -227,10 +227,10 @@ class Mocker( val mockingIsPossible = when (mockInfo) { is UtFieldMockInfo, - is UtObjectMockInfo -> applicationContext.mockFrameworkInstalled + is UtObjectMockInfo -> mockerContext.mockFrameworkInstalled is UtNewInstanceMockInfo, is UtStaticMethodMockInfo, - is UtStaticObjectMockInfo -> applicationContext.staticsMockingIsConfigured + is UtStaticObjectMockInfo -> mockerContext.staticsMockingIsConfigured } val mockingIsForcedAndPossible = mockAlways(mockedValue.type) && mockingIsPossible diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt index fc213b52de..38c7e71d65 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt @@ -116,7 +116,9 @@ import org.utbot.framework.UtSettings import org.utbot.framework.UtSettings.preferredCexOption import org.utbot.framework.UtSettings.substituteStaticsWithSymbolicVariable import org.utbot.framework.isFromTrustedLibrary -import org.utbot.framework.plugin.api.ApplicationContext +import org.utbot.framework.context.ApplicationContext +import org.utbot.framework.context.NonNullSpeculator +import org.utbot.framework.context.TypeReplacer import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.ExecutableId import org.utbot.framework.plugin.api.FieldId @@ -239,7 +241,8 @@ class Traverser( internal val typeResolver: TypeResolver, private val globalGraph: InterProceduralUnitGraph, private val mocker: Mocker, - private val applicationContext: ApplicationContext, + private val typeReplacer: TypeReplacer, + private val nonNullSpeculator: NonNullSpeculator, private val taintContext: TaintContext, ) : UtContextInitializer() { @@ -1393,8 +1396,8 @@ class Traverser( // However, if we have the restriction on implementor type (it may be obtained // from Spring bean definitions, for example), we can just create a symbolic object // with hard constraint on the mentioned type. - val replacedClassId = when (applicationContext.typeReplacementMode) { - KnownImplementor -> applicationContext.replaceTypeIfNeeded(type) + val replacedClassId = when (typeReplacer.typeReplacementMode) { + KnownImplementor -> typeReplacer.replaceTypeIfNeeded(type) AnyImplementor, NoImplementors -> null } @@ -1512,7 +1515,7 @@ class Traverser( return createMockedObject(addr, type, mockInfoGenerator, nullEqualityConstraint) } - val concreteImplementation: Concrete? = when (applicationContext.typeReplacementMode) { + val concreteImplementation: Concrete? = when (typeReplacer.typeReplacementMode) { AnyImplementor -> findConcreteImplementation(addr, type, typeHardConstraint) // If our type is not abstract, both in `KnownImplementors` and `NoImplementors` mode, @@ -2336,12 +2339,8 @@ class Traverser( * See more detailed documentation in [ApplicationContext] mentioned methods. */ private fun checkAndMarkLibraryFieldSpeculativelyNotNull(field: SootField, createdField: SymbolicValue) { - if (applicationContext.avoidSpeculativeNotNullChecks(field) || - !applicationContext.speculativelyCannotProduceNullPointerException(field, methodUnderTest.classId)) { - return - } - - markAsSpeculativelyNotNull(createdField.addr) + if (nonNullSpeculator.speculativelyCannotProduceNullPointerException(field, methodUnderTest.classId)) + markAsSpeculativelyNotNull(createdField.addr) } private fun createArray(pName: String, type: ArrayType): ArrayValue { diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt index d552d32c14..0cebe6c6ad 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.spring.SpringApplicationContext import org.utbot.framework.plugin.api.* import org.utbot.framework.plugin.api.Step import org.utbot.framework.plugin.api.util.* @@ -52,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 @@ -115,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() @@ -141,7 +145,7 @@ class UtBotSymbolicEngine( hierarchy, chosenClassesToMockAlways, MockListenerController(controller), - applicationContext = applicationContext, + mockerContext = applicationContext.mockerContext, ) fun attachMockListener(mockListener: MockListener) = mocker.mockListenerController?.attach(mockListener) @@ -177,7 +181,8 @@ class UtBotSymbolicEngine( typeResolver, globalGraph, mocker, - applicationContext, + applicationContext.typeReplacer, + applicationContext.nonNullSpeculator, taintContext, ) @@ -186,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/ApplicationContext.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/context/ApplicationContext.kt new file mode 100644 index 0000000000..51ce384a20 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/ApplicationContext.kt @@ -0,0 +1,14 @@ +package org.utbot.framework.context + +import org.utbot.framework.plugin.api.CodeGenerationContext + +interface ApplicationContext : CodeGenerationContext { + val mockerContext: MockerContext + val typeReplacer: TypeReplacer + val nonNullSpeculator: NonNullSpeculator + + 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..ec04d58132 --- /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 instrumentationFactory: UtExecutionInstrumentation.Factory<*> + // 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/MockerContext.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/context/MockerContext.kt new file mode 100644 index 0000000000..a85f591f5d --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/MockerContext.kt @@ -0,0 +1,13 @@ +package org.utbot.framework.context + +interface MockerContext { + /** + * Shows if we have installed framework dependencies + */ + val mockFrameworkInstalled: Boolean + + /** + * Shows if we have installed static mocking tools + */ + val staticsMockingIsConfigured: Boolean +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/context/NonNullSpeculator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/context/NonNullSpeculator.kt new file mode 100644 index 0000000000..3c4830cd45 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/NonNullSpeculator.kt @@ -0,0 +1,17 @@ +package org.utbot.framework.context + +import org.utbot.framework.plugin.api.ClassId +import soot.SootField + +interface NonNullSpeculator { + /** + * Checks whether accessing [field] (with a method invocation or field access) speculatively + * cannot produce [NullPointerException] (according to its finality or accessibility). + * + * @see docs/SpeculativeFieldNonNullability.md for more information. + */ + fun speculativelyCannotProduceNullPointerException( + field: SootField, + classUnderTest: ClassId, + ): Boolean +} diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/context/TypeReplacer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/context/TypeReplacer.kt new file mode 100644 index 0000000000..52c3ee8604 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/TypeReplacer.kt @@ -0,0 +1,18 @@ +package org.utbot.framework.context + +import org.utbot.framework.plugin.api.ClassId +import org.utbot.framework.plugin.api.TypeReplacementMode +import soot.RefType + +interface TypeReplacer { + /** + * Shows if there are any restrictions on type implementors. + */ + val typeReplacementMode: TypeReplacementMode + + /** + * Finds a type to replace the original abstract type + * if it is guided with some additional information. + */ + fun replaceTypeIfNeeded(type: RefType): ClassId? +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/context/simple/SimpleApplicationContext.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/context/simple/SimpleApplicationContext.kt new file mode 100644 index 0000000000..3b35ddca46 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/simple/SimpleApplicationContext.kt @@ -0,0 +1,21 @@ +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 + +/** + * A context to use when no specific data is required. + */ +class SimpleApplicationContext( + override val mockerContext: MockerContext, + override val typeReplacer: TypeReplacer = SimpleTypeReplacer(), + override val nonNullSpeculator: NonNullSpeculator = SimpleNonNullSpeculator() +) : ApplicationContext { + override fun createConcreteExecutionContext( + fullClasspath: String, + classpathWithoutDependencies: String + ): ConcreteExecutionContext = SimpleConcreteExecutionContext(fullClasspath, 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/simple/SimpleMockerContext.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/context/simple/SimpleMockerContext.kt new file mode 100644 index 0000000000..e7b0977d9e --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/simple/SimpleMockerContext.kt @@ -0,0 +1,16 @@ +package org.utbot.framework.context.simple + +import org.utbot.framework.context.MockerContext + +class SimpleMockerContext( + override val mockFrameworkInstalled: Boolean, + staticsMockingIsConfigured: Boolean +) : MockerContext { + /** + * NOTE: Can only be `true` when [mockFrameworkInstalled], because + * situation when mock framework is not installed but static mocking + * is configured is semantically incorrect. + */ + override val staticsMockingIsConfigured: Boolean = + mockFrameworkInstalled && staticsMockingIsConfigured +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/context/simple/SimpleNonNullSpeculator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/context/simple/SimpleNonNullSpeculator.kt new file mode 100644 index 0000000000..b1b8404190 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/simple/SimpleNonNullSpeculator.kt @@ -0,0 +1,17 @@ +package org.utbot.framework.context.simple + +import org.utbot.framework.UtSettings +import org.utbot.framework.context.NonNullSpeculator +import org.utbot.framework.isFromTrustedLibrary +import org.utbot.framework.plugin.api.ClassId +import soot.SootField + +class SimpleNonNullSpeculator : NonNullSpeculator { + override fun speculativelyCannotProduceNullPointerException( + field: SootField, + classUnderTest: ClassId, + ): Boolean = + !UtSettings.maximizeCoverageUsingReflection && + field.declaringClass.isFromTrustedLibrary() && + (field.isFinal || !field.isPublic) +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/context/simple/SimpleTypeReplacer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/context/simple/SimpleTypeReplacer.kt new file mode 100644 index 0000000000..300aea86e5 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/simple/SimpleTypeReplacer.kt @@ -0,0 +1,12 @@ +package org.utbot.framework.context.simple + +import org.utbot.framework.context.TypeReplacer +import org.utbot.framework.plugin.api.ClassId +import org.utbot.framework.plugin.api.TypeReplacementMode +import soot.RefType + +class SimpleTypeReplacer : TypeReplacer { + override val typeReplacementMode: TypeReplacementMode = TypeReplacementMode.AnyImplementor + + override fun replaceTypeIfNeeded(type: RefType): ClassId? = null +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringApplicationContext.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringApplicationContext.kt new file mode 100644 index 0000000000..15fab49219 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringApplicationContext.kt @@ -0,0 +1,24 @@ +package org.utbot.framework.context.spring + +import org.utbot.framework.context.ApplicationContext +import org.utbot.framework.plugin.api.BeanDefinitionData +import org.utbot.framework.plugin.api.ClassId +import org.utbot.framework.plugin.api.SpringCodeGenerationContext +import org.utbot.framework.plugin.api.SpringContextLoadingResult + +/** + * Data we get from Spring application context + * to manage engine and code generator behaviour. + */ +// TODO #2358 +interface SpringApplicationContext : ApplicationContext, SpringCodeGenerationContext { + /** + * Describes bean definitions (bean name, type, some optional additional data) + */ + val beanDefinitions: List + val injectedTypes: Set + val allInjectedSuperTypes: Set + + 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..790e9320c2 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringApplicationContextImpl.kt @@ -0,0 +1,99 @@ +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.ConcreteExecutionContext +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.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 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 + runCatching { + val beanClass = ClassId(beanDef.beanTypeName).jClass + classId.jClass.isAssignableFrom(beanClass) + }.getOrElse { false } + } + + // Classes representing concrete types that are actually used in Spring application + override val injectedTypes: Set + get() { + if (!areAllInjectedSuperTypesInitialized) { + for (beanTypeName in beanDefinitions.map { it.beanTypeName }) { + try { + val beanClass = utContext.classLoader.loadClass(beanTypeName) + if (!beanClass.isAbstract && !beanClass.isInterface && + !beanClass.isLocalClass && (!beanClass.isMemberClass || beanClass.isStatic)) { + _injectedTypes += beanClass.id + } + } catch (e: Throwable) { + // For some Spring beans (e.g. with anonymous classes) + // it is possible to have problems with classes loading. + when (e) { + is ClassNotFoundException, is NoClassDefFoundError, is IllegalAccessError -> + logger.warn { "Failed to load bean class for $beanTypeName (${e.message})" } + + else -> throw e + } + } + } + + // This is done to be sure that this storage is not empty after the first class loading iteration. + // So, even if all loaded classes were filtered out, we will not try to load them again. + areAllInjectedSuperTypesInitialized = true + } + + return _injectedTypes + } + + override val allInjectedSuperTypes: Set + get() { + if (!areInjectedTypesInitialized) { + _allInjectedSuperTypes = injectedTypes.flatMap { it.allSuperTypes() }.toSet() + areInjectedTypesInitialized = true + } + + return _allInjectedSuperTypes + } + + // the following properties help to imitate `by lazy` behaviour, do not use them directly + // (we can't use actual `by lazy` because communication via RD breaks it) + private var _allInjectedSuperTypes: Set = emptySet() + private var areAllInjectedSuperTypesInitialized : Boolean = false + + private val _injectedTypes = mutableSetOf() + private var areInjectedTypesInitialized: Boolean = false +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/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/context/spring/SpringNonNullSpeculator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringNonNullSpeculator.kt new file mode 100644 index 0000000000..5f49d7800d --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringNonNullSpeculator.kt @@ -0,0 +1,19 @@ +package org.utbot.framework.context.spring + +import org.utbot.framework.context.NonNullSpeculator +import org.utbot.framework.plugin.api.ClassId +import org.utbot.framework.plugin.api.classId +import org.utbot.framework.plugin.api.util.allDeclaredFieldIds +import org.utbot.framework.plugin.api.util.fieldId +import soot.SootField + +class SpringNonNullSpeculator( + private val delegateNonNullSpeculator: NonNullSpeculator, + private val springApplicationContext: SpringApplicationContext +) : NonNullSpeculator { + override fun speculativelyCannotProduceNullPointerException(field: SootField, classUnderTest: ClassId): Boolean = + // TODO add ` || delegateNonNullSpeculator.speculativelyCannotProduceNullPointerException(field, classUnderTest)` + // (TODO is added as a part of only equivalent transformations refactoring PR and should be completed in the follow up PR) + field.fieldId in classUnderTest.allDeclaredFieldIds && field.type.classId !in springApplicationContext.allInjectedSuperTypes + +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringTypeReplacer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringTypeReplacer.kt new file mode 100644 index 0000000000..78362c7520 --- /dev/null +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/context/spring/SpringTypeReplacer.kt @@ -0,0 +1,26 @@ +package org.utbot.framework.context.spring + +import org.utbot.framework.context.TypeReplacer +import org.utbot.framework.plugin.api.ClassId +import org.utbot.framework.plugin.api.TypeReplacementMode +import org.utbot.framework.plugin.api.id +import org.utbot.framework.plugin.api.isAbstractType +import org.utbot.framework.plugin.api.util.isSubtypeOf +import soot.RefType + +class SpringTypeReplacer( + private val delegateTypeReplacer: TypeReplacer, + private val springApplicationContext: SpringApplicationContext +) : TypeReplacer { + override val typeReplacementMode: TypeReplacementMode = + // TODO add ` || delegateTypeReplacer.typeReplacementMode == TypeReplacementMode.KnownImplementor` + // (TODO is added as a part of only equivalent transformations refactoring PR and should be completed in the follow up PR) + if (springApplicationContext.beanDefinitions.isNotEmpty()) TypeReplacementMode.KnownImplementor + else TypeReplacementMode.NoImplementors + + override fun replaceTypeIfNeeded(type: RefType): ClassId? = + // TODO add `delegateTypeReplacer.replaceTypeIfNeeded(type) ?: ` + // (TODO is added as a part of only equivalent transformations refactoring PR and should be completed in the follow up PR) + if (type.isAbstractType) springApplicationContext.injectedTypes.singleOrNull { it.isSubtypeOf(type.id) } + else null +} \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/coverage/CoverageCalculator.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/coverage/CoverageCalculator.kt index cfefe3c12e..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 ba78166b30..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 @@ -25,6 +25,10 @@ import org.utbot.framework.UtSettings.checkSolverTimeoutMillis import org.utbot.framework.UtSettings.disableCoroutinesDebug import org.utbot.framework.UtSettings.utBotGenerationTimeoutInMillis import org.utbot.framework.UtSettings.warmupConcreteExecution +import org.utbot.framework.context.ApplicationContext +import org.utbot.framework.context.simple.SimpleApplicationContext +import org.utbot.framework.context.simple.SimpleMockerContext +import org.utbot.framework.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 @@ -39,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 @@ -68,29 +73,43 @@ open class TestCaseGenerator( val engineActions: MutableList<(UtBotSymbolicEngine) -> Unit> = mutableListOf(), val isCanceled: () -> Boolean = { false }, val forceSootReload: Boolean = true, - val applicationContext: ApplicationContext = ApplicationContext(), + val applicationContext: ApplicationContext = SimpleApplicationContext( + SimpleMockerContext( + mockFrameworkInstalled = true, + staticsMockingIsConfigured = true + ) + ), ) { private val logger: KLogger = KotlinLogging.logger {} private val timeoutLogger: KLogger = KotlinLogging.logger(logger.name + ".timeout") - private val executionInstrumentation by lazy { + private val concreteExecutionContext = applicationContext.createConcreteExecutionContext( + fullClasspath = classpathForEngine, + classpathWithoutDependencies = buildDirs.joinToString(File.pathSeparator) + ) + + 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) @@ -114,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() @@ -149,8 +168,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 { @@ -185,10 +204,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() @@ -321,7 +340,7 @@ open class TestCaseGenerator( mockStrategy = mockStrategyApi.toModel(), chosenClassesToMockAlways = chosenClassesToMockAlways, applicationContext = applicationContext, - executionInstrumentation = executionInstrumentation, + executionInstrumentationFactory = executionInstrumentationFactory, solverTimeoutInMillis = executionTimeEstimator.updatedSolverCheckTimeoutMillis, userTaintConfigurationProvider = userTaintConfigurationProvider, ) @@ -457,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-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-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") 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..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 @@ -48,6 +48,9 @@ import org.utbot.framework.CancellationStrategyType.NONE import org.utbot.framework.CancellationStrategyType.SAVE_PROCESSED_RESULTS import org.utbot.framework.UtSettings import org.utbot.framework.codegen.domain.ProjectType.* +import org.utbot.framework.context.simple.SimpleApplicationContext +import org.utbot.framework.context.simple.SimpleMockerContext +import org.utbot.framework.context.spring.SpringApplicationContextImpl import org.utbot.framework.plugin.api.* import org.utbot.framework.plugin.api.SpringSettings.* import org.utbot.framework.plugin.api.SpringConfiguration.* @@ -260,6 +263,9 @@ object UtTestsDialogProcessor { process.terminateOnException { _ -> val classpathForClassLoader = buildDirs + classpathList process.setupUtContext(classpathForClassLoader) + val simpleApplicationContext = SimpleApplicationContext( + SimpleMockerContext(mockFrameworkInstalled, staticMockingConfigured) + ) val applicationContext = when (model.projectType) { Spring -> { val beanDefinitions = @@ -273,21 +279,17 @@ object UtTestsDialogProcessor { } } - val shouldUseImplementors = beanDefinitions.isNotEmpty() - val clarifiedBeanDefinitions = clarifyBeanDefinitionReturnTypes(beanDefinitions, project) - SpringApplicationContext( - mockFrameworkInstalled, - staticMockingConfigured, + SpringApplicationContextImpl( + simpleApplicationContext, clarifiedBeanDefinitions, - shouldUseImplementors, model.springTestType, model.springSettings, ) } - else -> ApplicationContext(mockFrameworkInstalled, staticMockingConfigured) + else -> simpleApplicationContext } process.createTestGenerator( buildDirs, 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..a26c9a6f52 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,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.simple.SimpleApplicationContext +import org.utbot.framework.context.simple.SimpleMockerContext +import org.utbot.framework.context.spring.SpringApplicationContextImpl import org.utbot.framework.plugin.api.* import org.utbot.framework.plugin.api.util.UtContext import org.utbot.framework.plugin.api.util.description @@ -268,10 +270,13 @@ class TestCodeGeneratorPipeline(private val testInfrastructureConfiguration: Tes parameterizedTestSource = parametrizedTestSource, runtimeExceptionTestsBehaviour = runtimeExceptionTestsBehaviour, enableTestsTimeout = enableTestsTimeout, - springCodeGenerationContext = SpringApplicationContext( - mockInstalled = true, - staticsMockingIsConfigured = true, - shouldUseImplementors = false, + springCodeGenerationContext = SpringApplicationContextImpl( + SimpleApplicationContext( + SimpleMockerContext( + mockFrameworkInstalled = true, + staticsMockingIsConfigured = true + ) + ), springTestType = SpringTestType.UNIT_TEST, springSettings = SpringSettings.AbsentSpringSettings(), )