From 613787d34ebca09033ae215df8f5084861419c9d Mon Sep 17 00:00:00 2001 From: Egor Kulikov Date: Fri, 17 Feb 2023 14:46:31 +0300 Subject: [PATCH 1/5] Transfer Spring application data from UI to engine --- .../org/utbot/framework/plugin/api/Api.kt | 10 +++++ .../main/kotlin/org/utbot/engine/Traverser.kt | 8 ++++ .../org/utbot/engine/UtBotSymbolicEngine.kt | 2 + .../framework/plugin/api/TestCaseGenerator.kt | 13 ++++++- .../framework/process/EngineProcessMain.kt | 3 ++ .../generated/EngineProcessModel.Generated.kt | 38 +++++++++++-------- .../generator/UtTestsDialogProcessor.kt | 11 +++++- .../org/utbot/rd/models/EngineProcessModel.kt | 1 + 8 files changed, 67 insertions(+), 19 deletions(-) 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 1813769347..23ffcfb205 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 @@ -1147,6 +1147,16 @@ open class TypeParameters(val parameters: List = emptyList()) class WildcardTypeParameter : TypeParameters(emptyList()) +/** + * Data we get from Spring application context + * to manage engine and code generator behaviour. + * + * @param beanQualifiedNames describes fqn of injected classes + */ +data class SpringApplicationData( + val beanQualifiedNames: List = emptyList(), +) + interface CodeGenerationSettingItem { val id: String val displayName: String 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 681d0dd904..4f0e1ee019 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt @@ -120,6 +120,7 @@ import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.ExecutableId import org.utbot.framework.plugin.api.FieldId import org.utbot.framework.plugin.api.MethodId +import org.utbot.framework.plugin.api.SpringApplicationData import org.utbot.framework.plugin.api.classId import org.utbot.framework.plugin.api.id import org.utbot.framework.plugin.api.util.executable @@ -237,6 +238,7 @@ class Traverser( internal val typeResolver: TypeResolver, private val globalGraph: InterProceduralUnitGraph, private val mocker: Mocker, + private val springApplicationData: SpringApplicationData?, ) : UtContextInitializer() { private val visitedStmts: MutableSet = mutableSetOf() @@ -273,6 +275,12 @@ class Traverser( internal val objectCounter = ObjectCounter(TypeRegistry.objectCounterInitialValue) + private val springInjectedClasses: List by lazy { + springApplicationData + ?.let { it.beanQualifiedNames.map { fqn -> classLoader.loadClass(fqn).id } } + ?: emptyList() + } + private fun findNewAddr(insideStaticInitializer: Boolean): UtAddrExpression { val newAddr = objectCounter.createNewAddr() // return negative address for objects created inside static initializer 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 ef6202d621..79ddb94bc9 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt @@ -105,6 +105,7 @@ class UtBotSymbolicEngine( dependencyPaths: String, val mockStrategy: MockStrategy = NO_MOCKS, chosenClassesToMockAlways: Set, + springApplicationData: SpringApplicationData?, private val solverTimeoutInMillis: Int = checkSolverTimeoutMillis ) : UtContextInitializer() { private val graph = methodUnderTest.sootMethod.jimpleBody().apply { @@ -144,6 +145,7 @@ class UtBotSymbolicEngine( typeResolver, globalGraph, mocker, + springApplicationData, ) //HACK (long strings) 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 454c3e5865..46e21c3655 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 @@ -61,6 +61,7 @@ open class TestCaseGenerator( val engineActions: MutableList<(UtBotSymbolicEngine) -> Unit> = mutableListOf(), val isCanceled: () -> Boolean = { false }, val forceSootReload: Boolean = true, + private val springApplicationData: SpringApplicationData? = null, ) { private val logger: KLogger = KotlinLogging.logger {} private val timeoutLogger: KLogger = KotlinLogging.logger(logger.name + ".timeout") @@ -112,7 +113,14 @@ open class TestCaseGenerator( executionTimeEstimator: ExecutionTimeEstimator = ExecutionTimeEstimator(utBotGenerationTimeoutInMillis, 1) ): Flow { try { - val engine = createSymbolicEngine(controller, method, mockStrategy, chosenClassesToMockAlways, executionTimeEstimator) + val engine = createSymbolicEngine( + controller, + method, + mockStrategy, + chosenClassesToMockAlways, + springApplicationData = null, + executionTimeEstimator, + ) engineActions.map { engine.apply(it) } engineActions.clear() return defaultTestFlow(engine, executionTimeEstimator.userTimeout) @@ -159,6 +167,7 @@ open class TestCaseGenerator( method, mockStrategy, chosenClassesToMockAlways, + springApplicationData, executionTimeEstimator ) @@ -248,6 +257,7 @@ open class TestCaseGenerator( method: ExecutableId, mockStrategyApi: MockStrategyApi, chosenClassesToMockAlways: Set, + springApplicationData: SpringApplicationData?, executionTimeEstimator: ExecutionTimeEstimator ): UtBotSymbolicEngine { logger.debug("Starting symbolic execution for $method --$mockStrategyApi--") @@ -258,6 +268,7 @@ open class TestCaseGenerator( dependencyPaths = dependencyPaths, mockStrategy = mockStrategyApi.toModel(), chosenClassesToMockAlways = chosenClassesToMockAlways, + springApplicationData = springApplicationData, solverTimeoutInMillis = executionTimeEstimator.updatedSolverCheckTimeoutMillis ) } 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 49fc11bab7..b96472fc0e 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 @@ -83,10 +83,13 @@ private fun EngineProcessModel.setup(kryoHelper: KryoHelper, watchdog: IdleWatch watchdog.measureTimeForActiveCall(createTestGenerator, "Creating Test Generator") { params -> AnalyticsConfigureUtil.configureML() Instrumenter.adapter = RdInstrumenter(realProtocol.rdInstrumenterAdapter) + val springApplicationData: SpringApplicationData = kryoHelper.readObject(params.springApplicationData) + testGenerator = TestCaseGenerator(buildDirs = params.buildDir.map { Paths.get(it) }, classpath = params.classpath, dependencyPaths = params.dependencyPaths, jdkInfo = JdkInfo(Paths.get(params.jdkInfo.path), params.jdkInfo.version), + springApplicationData = springApplicationData, isCanceled = { runBlocking { model.isCancelled.startSuspending(Unit) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessModel.Generated.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessModel.Generated.kt index 76ac332ac6..f9e1ddd378 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessModel.Generated.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessModel.Generated.kt @@ -72,7 +72,7 @@ class EngineProcessModel private constructor( } - const val serializationHash = 5025678608993948804L + const val serializationHash = 3473047730945121616L } override val serializersOwner: ISerializersOwner get() = EngineProcessModel @@ -173,7 +173,7 @@ val IProtocol.engineProcessModel get() = getOrCreateExtension(EngineProcessModel /** - * #### Generated from [EngineProcessModel.kt:101] + * #### Generated from [EngineProcessModel.kt:102] */ data class FindMethodParamNamesArguments ( val classId: ByteArray, @@ -236,7 +236,7 @@ data class FindMethodParamNamesArguments ( /** - * #### Generated from [EngineProcessModel.kt:105] + * #### Generated from [EngineProcessModel.kt:106] */ data class FindMethodParamNamesResult ( val paramNames: ByteArray @@ -293,7 +293,7 @@ data class FindMethodParamNamesResult ( /** - * #### Generated from [EngineProcessModel.kt:94] + * #### Generated from [EngineProcessModel.kt:95] */ data class FindMethodsInClassMatchingSelectedArguments ( val classId: ByteArray, @@ -356,7 +356,7 @@ data class FindMethodsInClassMatchingSelectedArguments ( /** - * #### Generated from [EngineProcessModel.kt:98] + * #### Generated from [EngineProcessModel.kt:99] */ data class FindMethodsInClassMatchingSelectedResult ( val executableIds: ByteArray @@ -413,7 +413,7 @@ data class FindMethodsInClassMatchingSelectedResult ( /** - * #### Generated from [EngineProcessModel.kt:43] + * #### Generated from [EngineProcessModel.kt:44] */ data class GenerateParams ( val mockInstalled: Boolean, @@ -536,7 +536,7 @@ data class GenerateParams ( /** - * #### Generated from [EngineProcessModel.kt:61] + * #### Generated from [EngineProcessModel.kt:62] */ data class GenerateResult ( val notEmptyCases: Int, @@ -599,7 +599,7 @@ data class GenerateResult ( /** - * #### Generated from [EngineProcessModel.kt:113] + * #### Generated from [EngineProcessModel.kt:114] */ data class GenerateTestReportArgs ( val eventLogMessage: String?, @@ -692,7 +692,7 @@ data class GenerateTestReportArgs ( /** - * #### Generated from [EngineProcessModel.kt:122] + * #### Generated from [EngineProcessModel.kt:123] */ data class GenerateTestReportResult ( val notifyMessage: String, @@ -824,7 +824,7 @@ data class JdkInfo ( /** - * #### Generated from [EngineProcessModel.kt:89] + * #### Generated from [EngineProcessModel.kt:90] */ data class MethodDescription ( val name: String, @@ -893,7 +893,7 @@ data class MethodDescription ( /** - * #### Generated from [EngineProcessModel.kt:65] + * #### Generated from [EngineProcessModel.kt:66] */ data class RenderParams ( val testSetsId: Long, @@ -1034,7 +1034,7 @@ data class RenderParams ( /** - * #### Generated from [EngineProcessModel.kt:82] + * #### Generated from [EngineProcessModel.kt:83] */ data class RenderResult ( val generatedCode: String, @@ -1097,7 +1097,7 @@ data class RenderResult ( /** - * #### Generated from [EngineProcessModel.kt:86] + * #### Generated from [EngineProcessModel.kt:87] */ data class SetupContextParams ( val classpathForUrlsClassloader: List @@ -1160,7 +1160,8 @@ data class TestGeneratorParams ( val buildDir: Array, val classpath: String?, val dependencyPaths: String, - val jdkInfo: JdkInfo + val jdkInfo: JdkInfo, + val springApplicationData: ByteArray ) : IPrintable { //companion @@ -1173,7 +1174,8 @@ data class TestGeneratorParams ( val classpath = buffer.readNullable { buffer.readString() } val dependencyPaths = buffer.readString() val jdkInfo = JdkInfo.read(ctx, buffer) - return TestGeneratorParams(buildDir, classpath, dependencyPaths, jdkInfo) + val springApplicationData = buffer.readByteArray() + return TestGeneratorParams(buildDir, classpath, dependencyPaths, jdkInfo, springApplicationData) } override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: TestGeneratorParams) { @@ -1181,6 +1183,7 @@ data class TestGeneratorParams ( buffer.writeNullable(value.classpath) { buffer.writeString(it) } buffer.writeString(value.dependencyPaths) JdkInfo.write(ctx, buffer, value.jdkInfo) + buffer.writeByteArray(value.springApplicationData) } @@ -1200,6 +1203,7 @@ data class TestGeneratorParams ( if (classpath != other.classpath) return false if (dependencyPaths != other.dependencyPaths) return false if (jdkInfo != other.jdkInfo) return false + if (!(springApplicationData contentEquals other.springApplicationData)) return false return true } @@ -1210,6 +1214,7 @@ data class TestGeneratorParams ( __r = __r*31 + if (classpath != null) classpath.hashCode() else 0 __r = __r*31 + dependencyPaths.hashCode() __r = __r*31 + jdkInfo.hashCode() + __r = __r*31 + springApplicationData.contentHashCode() return __r } //pretty print @@ -1220,6 +1225,7 @@ data class TestGeneratorParams ( print("classpath = "); classpath.print(printer); println() print("dependencyPaths = "); dependencyPaths.print(printer); println() print("jdkInfo = "); jdkInfo.print(printer); println() + print("springApplicationData = "); springApplicationData.print(printer); println() } printer.print(")") } @@ -1229,7 +1235,7 @@ data class TestGeneratorParams ( /** - * #### Generated from [EngineProcessModel.kt:108] + * #### Generated from [EngineProcessModel.kt:109] */ data class WriteSarifReportArguments ( val testSetsId: Long, 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 e7f9911d75..8f5fe28a89 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 @@ -27,6 +27,7 @@ import org.utbot.framework.CancellationStrategyType.SAVE_PROCESSED_RESULTS import org.utbot.framework.UtSettings import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.JavaDocCommentStyle +import org.utbot.framework.plugin.api.SpringApplicationData import org.utbot.framework.plugin.api.util.LockFile import org.utbot.framework.plugin.api.util.withStaticsSubstitutionRequired import org.utbot.framework.plugin.services.JdkInfoService @@ -168,6 +169,12 @@ object UtTestsDialogProcessor { psiClass.canonicalName to psiClass.containingFile.virtualFile.canonicalPath }.toMap() } + + // TODO: obtain bean definitions and other info from `utbot-spring-analyzer` + val springApplicationData = SpringApplicationData( + beanQualifiedNames = emptyList(), + ) + val process = EngineProcess.createBlocking(project, classNameToPath) process.terminateOnException { _ -> @@ -176,14 +183,14 @@ object UtTestsDialogProcessor { buildDirs, classpath, pluginJarsPath.joinToString(separator = File.pathSeparator), - JdkInfoService.provide() + JdkInfoService.provide(), + springApplicationData, ) { ApplicationManager.getApplication().runReadAction(Computable { indicator.isCanceled }) } - for (srcClass in model.srcClasses) { if (indicator.isCanceled) { when (UtSettings.cancellationStrategyType) { diff --git a/utbot-rd/src/main/rdgen/org/utbot/rd/models/EngineProcessModel.kt b/utbot-rd/src/main/rdgen/org/utbot/rd/models/EngineProcessModel.kt index 532962d55b..8001ce3969 100644 --- a/utbot-rd/src/main/rdgen/org/utbot/rd/models/EngineProcessModel.kt +++ b/utbot-rd/src/main/rdgen/org/utbot/rd/models/EngineProcessModel.kt @@ -39,6 +39,7 @@ object EngineProcessModel : Ext(EngineProcessRoot) { field("classpath", PredefinedType.string.nullable) field("dependencyPaths", PredefinedType.string) field("jdkInfo", jdkInfo) + field("springApplicationData", array(PredefinedType.byte)) } val generateParams = structdef { // mocks From cd15ebb8695efd4c5e0952e28f86e515524a53ac Mon Sep 17 00:00:00 2001 From: Egor Kulikov Date: Fri, 17 Feb 2023 15:25:42 +0300 Subject: [PATCH 2/5] Create a flow for Spring tests --- .../framework/plugin/api/TestCaseGenerator.kt | 2 +- .../utbot/framework/plugin/api/TestFlow.kt | 10 +++++++++ .../framework/process/EngineProcessMain.kt | 21 +++++++++++++------ 3 files changed, 26 insertions(+), 7 deletions(-) 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 46e21c3655..2f281ed531 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 @@ -61,7 +61,7 @@ open class TestCaseGenerator( val engineActions: MutableList<(UtBotSymbolicEngine) -> Unit> = mutableListOf(), val isCanceled: () -> Boolean = { false }, val forceSootReload: Boolean = true, - private val springApplicationData: SpringApplicationData? = null, + val springApplicationData: SpringApplicationData? = null, ) { private val logger: KLogger = KotlinLogging.logger {} private val timeoutLogger: KLogger = KotlinLogging.logger(logger.name + ".timeout") diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestFlow.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestFlow.kt index 2407407acd..72c76905bb 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestFlow.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestFlow.kt @@ -24,6 +24,16 @@ fun defaultTestFlow(timeout: Long) = testFlow { } } +/** + * Creates default flow for Spring application. + */ +fun defaultSpringFlow(timeout: Long) = testFlow { + isSymbolicEngineEnabled = true + generationTimeout = timeout + isFuzzingEnabled = false + fuzzingValue = 0.0 +} + /** * Creates default flow that uses [UtSettings] for customization. */ 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 b96472fc0e..c3a768dda8 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 @@ -108,16 +108,25 @@ private fun EngineProcessModel.setup(kryoHelper: KryoHelper, watchdog: IdleWatch if (!staticsMockingConfigured) { ForceStaticMockListener.create(testGenerator, conflictTriggers, cancelJob = true) } - val result = testGenerator.generate(methods, - MockStrategyApi.valueOf(params.mockStrategy), - kryoHelper.readObject(params.chosenClassesToMockAlways), - params.timeout, - generate = testFlow { + + val generateFlow = if (testGenerator.springApplicationData != null) { + defaultSpringFlow(params.generationTimeout) + } else { + testFlow { generationTimeout = params.generationTimeout isSymbolicEngineEnabled = params.isSymbolicEngineEnabled isFuzzingEnabled = params.isFuzzingEnabled fuzzingValue = params.fuzzingValue - }) + } + } + + val result = testGenerator.generate( + methods, + MockStrategyApi.valueOf(params.mockStrategy), + kryoHelper.readObject(params.chosenClassesToMockAlways), + params.timeout, + generate = generateFlow, + ) .summarizeAll(Paths.get(params.searchDirectory), null) .filterNot { it.executions.isEmpty() && it.errors.isEmpty() } From 4e661b4a91b9915e1f7756c843b21ea158c601aa Mon Sep 17 00:00:00 2001 From: Egor Kulikov Date: Fri, 17 Feb 2023 16:39:08 +0300 Subject: [PATCH 3/5] Introduce ApplicationContext and inherit Spring one from it. --- .../kotlin/org/utbot/framework/plugin/api/Api.kt | 9 +++++++-- .../main/kotlin/org/utbot/engine/Traverser.kt | 14 +++++++++----- .../org/utbot/engine/UtBotSymbolicEngine.kt | 4 ++-- .../framework/plugin/api/TestCaseGenerator.kt | 10 +++++----- .../utbot/framework/process/EngineProcessMain.kt | 11 +++++------ .../generated/EngineProcessModel.Generated.kt | 16 ++++++++-------- .../plugin/generator/UtTestsDialogProcessor.kt | 6 +++--- .../org/utbot/rd/models/EngineProcessModel.kt | 2 +- 8 files changed, 40 insertions(+), 32 deletions(-) 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 23ffcfb205..91454b0f0f 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 @@ -1147,15 +1147,20 @@ open class TypeParameters(val parameters: List = emptyList()) class WildcardTypeParameter : TypeParameters(emptyList()) +/** + * Additional data describing user project. + */ +interface ApplicationContext + /** * Data we get from Spring application context * to manage engine and code generator behaviour. * * @param beanQualifiedNames describes fqn of injected classes */ -data class SpringApplicationData( +data class SpringApplicationContext( val beanQualifiedNames: List = emptyList(), -) +): ApplicationContext interface CodeGenerationSettingItem { val id: String 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 4f0e1ee019..48bdb23636 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt @@ -116,11 +116,12 @@ import org.utbot.framework.UtSettings import org.utbot.framework.UtSettings.maximizeCoverageUsingReflection import org.utbot.framework.UtSettings.preferredCexOption import org.utbot.framework.UtSettings.substituteStaticsWithSymbolicVariable +import org.utbot.framework.plugin.api.ApplicationContext import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.ExecutableId import org.utbot.framework.plugin.api.FieldId import org.utbot.framework.plugin.api.MethodId -import org.utbot.framework.plugin.api.SpringApplicationData +import org.utbot.framework.plugin.api.SpringApplicationContext import org.utbot.framework.plugin.api.classId import org.utbot.framework.plugin.api.id import org.utbot.framework.plugin.api.util.executable @@ -238,7 +239,7 @@ class Traverser( internal val typeResolver: TypeResolver, private val globalGraph: InterProceduralUnitGraph, private val mocker: Mocker, - private val springApplicationData: SpringApplicationData?, + private val applicationContext: ApplicationContext?, ) : UtContextInitializer() { private val visitedStmts: MutableSet = mutableSetOf() @@ -276,9 +277,12 @@ class Traverser( internal val objectCounter = ObjectCounter(TypeRegistry.objectCounterInitialValue) private val springInjectedClasses: List by lazy { - springApplicationData - ?.let { it.beanQualifiedNames.map { fqn -> classLoader.loadClass(fqn).id } } - ?: emptyList() + when (applicationContext) { + is SpringApplicationContext -> { + applicationContext.beanQualifiedNames.map { fqn -> classLoader.loadClass(fqn).id } + } + else -> emptyList() + } } private fun findNewAddr(insideStaticInitializer: Boolean): UtAddrExpression { 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 79ddb94bc9..083efdde51 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt @@ -105,7 +105,7 @@ class UtBotSymbolicEngine( dependencyPaths: String, val mockStrategy: MockStrategy = NO_MOCKS, chosenClassesToMockAlways: Set, - springApplicationData: SpringApplicationData?, + applicationContext: ApplicationContext?, private val solverTimeoutInMillis: Int = checkSolverTimeoutMillis ) : UtContextInitializer() { private val graph = methodUnderTest.sootMethod.jimpleBody().apply { @@ -145,7 +145,7 @@ class UtBotSymbolicEngine( typeResolver, globalGraph, mocker, - springApplicationData, + applicationContext, ) //HACK (long strings) 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 2f281ed531..3977e178c7 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 @@ -61,7 +61,7 @@ open class TestCaseGenerator( val engineActions: MutableList<(UtBotSymbolicEngine) -> Unit> = mutableListOf(), val isCanceled: () -> Boolean = { false }, val forceSootReload: Boolean = true, - val springApplicationData: SpringApplicationData? = null, + val applicationContext: ApplicationContext? = null, ) { private val logger: KLogger = KotlinLogging.logger {} private val timeoutLogger: KLogger = KotlinLogging.logger(logger.name + ".timeout") @@ -118,7 +118,7 @@ open class TestCaseGenerator( method, mockStrategy, chosenClassesToMockAlways, - springApplicationData = null, + applicationContext = null, executionTimeEstimator, ) engineActions.map { engine.apply(it) } @@ -167,7 +167,7 @@ open class TestCaseGenerator( method, mockStrategy, chosenClassesToMockAlways, - springApplicationData, + applicationContext, executionTimeEstimator ) @@ -257,7 +257,7 @@ open class TestCaseGenerator( method: ExecutableId, mockStrategyApi: MockStrategyApi, chosenClassesToMockAlways: Set, - springApplicationData: SpringApplicationData?, + applicationContext: ApplicationContext?, executionTimeEstimator: ExecutionTimeEstimator ): UtBotSymbolicEngine { logger.debug("Starting symbolic execution for $method --$mockStrategyApi--") @@ -268,7 +268,7 @@ open class TestCaseGenerator( dependencyPaths = dependencyPaths, mockStrategy = mockStrategyApi.toModel(), chosenClassesToMockAlways = chosenClassesToMockAlways, - springApplicationData = springApplicationData, + applicationContext = applicationContext, solverTimeoutInMillis = executionTimeEstimator.updatedSolverCheckTimeoutMillis ) } 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 c3a768dda8..f4eb9c99f1 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 @@ -83,13 +83,13 @@ private fun EngineProcessModel.setup(kryoHelper: KryoHelper, watchdog: IdleWatch watchdog.measureTimeForActiveCall(createTestGenerator, "Creating Test Generator") { params -> AnalyticsConfigureUtil.configureML() Instrumenter.adapter = RdInstrumenter(realProtocol.rdInstrumenterAdapter) - val springApplicationData: SpringApplicationData = kryoHelper.readObject(params.springApplicationData) + val applicationContext: ApplicationContext = kryoHelper.readObject(params.applicationContext) testGenerator = TestCaseGenerator(buildDirs = params.buildDir.map { Paths.get(it) }, classpath = params.classpath, dependencyPaths = params.dependencyPaths, jdkInfo = JdkInfo(Paths.get(params.jdkInfo.path), params.jdkInfo.version), - springApplicationData = springApplicationData, + applicationContext = applicationContext, isCanceled = { runBlocking { model.isCancelled.startSuspending(Unit) @@ -109,10 +109,9 @@ private fun EngineProcessModel.setup(kryoHelper: KryoHelper, watchdog: IdleWatch ForceStaticMockListener.create(testGenerator, conflictTriggers, cancelJob = true) } - val generateFlow = if (testGenerator.springApplicationData != null) { - defaultSpringFlow(params.generationTimeout) - } else { - testFlow { + val generateFlow = when (testGenerator.applicationContext) { + is SpringApplicationContext -> defaultSpringFlow(params.generationTimeout) + else -> testFlow { generationTimeout = params.generationTimeout isSymbolicEngineEnabled = params.isSymbolicEngineEnabled isFuzzingEnabled = params.isFuzzingEnabled diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessModel.Generated.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessModel.Generated.kt index f9e1ddd378..9a136e45bd 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessModel.Generated.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/process/generated/EngineProcessModel.Generated.kt @@ -72,7 +72,7 @@ class EngineProcessModel private constructor( } - const val serializationHash = 3473047730945121616L + const val serializationHash = -4839464828913070560L } override val serializersOwner: ISerializersOwner get() = EngineProcessModel @@ -1161,7 +1161,7 @@ data class TestGeneratorParams ( val classpath: String?, val dependencyPaths: String, val jdkInfo: JdkInfo, - val springApplicationData: ByteArray + val applicationContext: ByteArray ) : IPrintable { //companion @@ -1174,8 +1174,8 @@ data class TestGeneratorParams ( val classpath = buffer.readNullable { buffer.readString() } val dependencyPaths = buffer.readString() val jdkInfo = JdkInfo.read(ctx, buffer) - val springApplicationData = buffer.readByteArray() - return TestGeneratorParams(buildDir, classpath, dependencyPaths, jdkInfo, springApplicationData) + val applicationContext = buffer.readByteArray() + return TestGeneratorParams(buildDir, classpath, dependencyPaths, jdkInfo, applicationContext) } override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: TestGeneratorParams) { @@ -1183,7 +1183,7 @@ data class TestGeneratorParams ( buffer.writeNullable(value.classpath) { buffer.writeString(it) } buffer.writeString(value.dependencyPaths) JdkInfo.write(ctx, buffer, value.jdkInfo) - buffer.writeByteArray(value.springApplicationData) + buffer.writeByteArray(value.applicationContext) } @@ -1203,7 +1203,7 @@ data class TestGeneratorParams ( if (classpath != other.classpath) return false if (dependencyPaths != other.dependencyPaths) return false if (jdkInfo != other.jdkInfo) return false - if (!(springApplicationData contentEquals other.springApplicationData)) return false + if (!(applicationContext contentEquals other.applicationContext)) return false return true } @@ -1214,7 +1214,7 @@ data class TestGeneratorParams ( __r = __r*31 + if (classpath != null) classpath.hashCode() else 0 __r = __r*31 + dependencyPaths.hashCode() __r = __r*31 + jdkInfo.hashCode() - __r = __r*31 + springApplicationData.contentHashCode() + __r = __r*31 + applicationContext.contentHashCode() return __r } //pretty print @@ -1225,7 +1225,7 @@ data class TestGeneratorParams ( print("classpath = "); classpath.print(printer); println() print("dependencyPaths = "); dependencyPaths.print(printer); println() print("jdkInfo = "); jdkInfo.print(printer); println() - print("springApplicationData = "); springApplicationData.print(printer); println() + print("applicationContext = "); applicationContext.print(printer); println() } printer.print(")") } 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 8f5fe28a89..329e17e20c 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 @@ -27,7 +27,7 @@ import org.utbot.framework.CancellationStrategyType.SAVE_PROCESSED_RESULTS import org.utbot.framework.UtSettings import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.JavaDocCommentStyle -import org.utbot.framework.plugin.api.SpringApplicationData +import org.utbot.framework.plugin.api.SpringApplicationContext import org.utbot.framework.plugin.api.util.LockFile import org.utbot.framework.plugin.api.util.withStaticsSubstitutionRequired import org.utbot.framework.plugin.services.JdkInfoService @@ -171,7 +171,7 @@ object UtTestsDialogProcessor { } // TODO: obtain bean definitions and other info from `utbot-spring-analyzer` - val springApplicationData = SpringApplicationData( + val springApplicationContext = SpringApplicationContext( beanQualifiedNames = emptyList(), ) @@ -184,7 +184,7 @@ object UtTestsDialogProcessor { classpath, pluginJarsPath.joinToString(separator = File.pathSeparator), JdkInfoService.provide(), - springApplicationData, + springApplicationContext, ) { ApplicationManager.getApplication().runReadAction(Computable { indicator.isCanceled diff --git a/utbot-rd/src/main/rdgen/org/utbot/rd/models/EngineProcessModel.kt b/utbot-rd/src/main/rdgen/org/utbot/rd/models/EngineProcessModel.kt index 8001ce3969..08484c8273 100644 --- a/utbot-rd/src/main/rdgen/org/utbot/rd/models/EngineProcessModel.kt +++ b/utbot-rd/src/main/rdgen/org/utbot/rd/models/EngineProcessModel.kt @@ -39,7 +39,7 @@ object EngineProcessModel : Ext(EngineProcessRoot) { field("classpath", PredefinedType.string.nullable) field("dependencyPaths", PredefinedType.string) field("jdkInfo", jdkInfo) - field("springApplicationData", array(PredefinedType.byte)) + field("applicationContext", array(PredefinedType.byte)) } val generateParams = structdef { // mocks From 2b988c94bb84e507fc51e49dfb3c6ac2abbbaf76 Mon Sep 17 00:00:00 2001 From: Egor Kulikov Date: Fri, 17 Feb 2023 16:50:50 +0300 Subject: [PATCH 4/5] Fix compilation --- .../kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 c63682b2b5..b0eec3639b 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 @@ -188,6 +188,7 @@ class EngineProcess private constructor(val project: Project, private val classN classPath: String?, dependencyPaths: String, jdkInfo: JdkInfo, + applicationContext: ApplicationContext, isCancelled: (Unit) -> Boolean ) { assertReadAccessNotAllowed() @@ -199,7 +200,8 @@ class EngineProcess private constructor(val project: Project, private val classN buildDir.toTypedArray(), classPath, dependencyPaths, - JdkInfo(jdkInfo.path.pathString, jdkInfo.version) + JdkInfo(jdkInfo.path.pathString, jdkInfo.version), + kryoHelper.writeObject(applicationContext) ) engineModel.createTestGenerator.startBlocking(params) } From 269205d649430dac314b485dd3cf38c5bfd435e0 Mon Sep 17 00:00:00 2001 From: Egor Kulikov Date: Fri, 17 Feb 2023 17:17:44 +0300 Subject: [PATCH 5/5] Store springInjectedClasses in SpringApplicationContext --- .../main/kotlin/org/utbot/framework/plugin/api/Api.kt | 7 ++++++- .../src/main/kotlin/org/utbot/engine/Traverser.kt | 9 +-------- 2 files changed, 7 insertions(+), 9 deletions(-) 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 91454b0f0f..cc305f1b02 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 @@ -55,6 +55,7 @@ import java.io.File import kotlin.contracts.ExperimentalContracts import kotlin.contracts.contract import org.utbot.common.isAbstract +import org.utbot.framework.plugin.api.util.utContext const val SYMBOLIC_NULL_ADDR: Int = 0 @@ -1160,7 +1161,11 @@ interface ApplicationContext */ data class SpringApplicationContext( val beanQualifiedNames: List = emptyList(), -): ApplicationContext +): ApplicationContext { + private val springInjectedClasses: List by lazy { + beanQualifiedNames.map { fqn -> utContext.classLoader.loadClass(fqn).id } + } +} interface CodeGenerationSettingItem { val id: String 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 48bdb23636..db74e9d5f8 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt @@ -276,14 +276,7 @@ class Traverser( internal val objectCounter = ObjectCounter(TypeRegistry.objectCounterInitialValue) - private val springInjectedClasses: List by lazy { - when (applicationContext) { - is SpringApplicationContext -> { - applicationContext.beanQualifiedNames.map { fqn -> classLoader.loadClass(fqn).id } - } - else -> emptyList() - } - } + private fun findNewAddr(insideStaticInitializer: Boolean): UtAddrExpression { val newAddr = objectCounter.createNewAddr()