Skip to content

Commit 1a6244c

Browse files
committed
Transfer further configuration data from UI to application context
1 parent b6d3178 commit 1a6244c

File tree

11 files changed

+125
-57
lines changed

11 files changed

+125
-57
lines changed

utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1157,7 +1157,7 @@ class WildcardTypeParameter : TypeParameters(emptyList())
11571157
* @param mockFrameworkInstalled shows if we have installed framework dependencies
11581158
* @param staticsMockingIsConfigured shows if we have installed static mocking tools
11591159
*/
1160-
open class StandardApplicationContext(
1160+
open class ApplicationContext(
11611161
val mockFrameworkInstalled: Boolean = true,
11621162
staticsMockingIsConfigured: Boolean = true,
11631163
) {
@@ -1191,13 +1191,15 @@ open class StandardApplicationContext(
11911191
* Data we get from Spring application context
11921192
* to manage engine and code generator behaviour.
11931193
*
1194-
* @param beanQualifiedNames describes fqn of injected classes
1194+
* @param beanQualifiedNames describes fqn of types from bean definitions
1195+
* @param shouldUseImplementors describes it we want to replace interfaces with injected types or not
11951196
*/
11961197
class SpringApplicationContext(
11971198
mockInstalled: Boolean,
11981199
staticsMockingIsConfigured: Boolean,
11991200
val beanQualifiedNames: List<String> = emptyList(),
1200-
): StandardApplicationContext(mockInstalled, staticsMockingIsConfigured) {
1201+
val shouldUseImplementors: Boolean,
1202+
): ApplicationContext(mockInstalled, staticsMockingIsConfigured) {
12011203
private val springInjectedClasses: List<ClassId> by lazy {
12021204
beanQualifiedNames
12031205
.map { fqn -> utContext.classLoader.loadClass(fqn) }
@@ -1210,7 +1212,8 @@ class SpringApplicationContext(
12101212
* if there is the unique implementor in bean definitions.
12111213
*/
12121214
override fun replaceTypeIfNeeded(type: RefType): ClassId? =
1213-
if (type.sootClass.isInterface) {
1215+
if (shouldUseImplementors &&
1216+
(type.sootClass.isInterface || type.sootClass.isAbstract)) {
12141217
springInjectedClasses.singleOrNull { it.isSubtypeOf(type.id) }
12151218
} else {
12161219
null

utbot-framework/src/main/kotlin/org/utbot/engine/Mocks.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import kotlinx.collections.immutable.persistentListOf
1919
import org.utbot.common.nameOfPackage
2020
import org.utbot.engine.types.OBJECT_TYPE
2121
import org.utbot.engine.util.mockListeners.MockListenerController
22-
import org.utbot.framework.plugin.api.StandardApplicationContext
22+
import org.utbot.framework.plugin.api.ApplicationContext
2323
import org.utbot.framework.plugin.api.util.isInaccessibleViaReflection
2424
import soot.BooleanType
2525
import soot.RefType
@@ -168,7 +168,7 @@ class Mocker(
168168
private val hierarchy: Hierarchy,
169169
chosenClassesToMockAlways: Set<ClassId>,
170170
internal val mockListenerController: MockListenerController? = null,
171-
private val applicationContext: StandardApplicationContext,
171+
private val applicationContext: ApplicationContext,
172172
) {
173173
private val mocksAreDesired: Boolean = strategy != MockStrategy.NO_MOCKS
174174

utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt

Lines changed: 45 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,12 @@ import org.utbot.framework.UtSettings
116116
import org.utbot.framework.UtSettings.maximizeCoverageUsingReflection
117117
import org.utbot.framework.UtSettings.preferredCexOption
118118
import org.utbot.framework.UtSettings.substituteStaticsWithSymbolicVariable
119-
import org.utbot.framework.plugin.api.StandardApplicationContext
119+
import org.utbot.framework.plugin.api.ApplicationContext
120120
import org.utbot.framework.plugin.api.ClassId
121121
import org.utbot.framework.plugin.api.ExecutableId
122122
import org.utbot.framework.plugin.api.FieldId
123123
import org.utbot.framework.plugin.api.MethodId
124+
import org.utbot.framework.plugin.api.SpringApplicationContext
124125
import org.utbot.framework.plugin.api.classId
125126
import org.utbot.framework.plugin.api.id
126127
import org.utbot.framework.plugin.api.util.executable
@@ -239,7 +240,7 @@ class Traverser(
239240
internal val typeResolver: TypeResolver,
240241
private val globalGraph: InterProceduralUnitGraph,
241242
private val mocker: Mocker,
242-
private val applicationContext: StandardApplicationContext,
243+
private val applicationContext: ApplicationContext,
243244
) : UtContextInitializer() {
244245

245246
private val visitedStmts: MutableSet<Stmt> = mutableSetOf()
@@ -1480,56 +1481,62 @@ class Traverser(
14801481
return it
14811482
}
14821483

1483-
if (typeStorage.possibleConcreteTypes.isEmpty()) {
1484-
requireNotNull(mockInfoGenerator) {
1485-
"An object with $addr and $type doesn't have concrete possible types," +
1486-
"but there is no mock info generator provided to construct a mock value."
1487-
}
1484+
// In Spring application we may rely on concrete types only
1485+
// if this type is obtained from bean definitions,
1486+
// this has already been checked in
1487+
val allowsConcreteTypes = applicationContext !is SpringApplicationContext
14881488

1489-
val mockInfo = mockInfoGenerator.generate(addr)
1490-
val mockedObjectInfo = mocker.forceMock(type, mockInfoGenerator.generate(addr))
1491-
1492-
val mockedObject: ObjectValue = when (mockedObjectInfo) {
1493-
is NoMock -> error("Value must be mocked after the force mock")
1494-
is ExpectedMock -> mockedObjectInfo.value
1495-
is UnexpectedMock -> {
1496-
// if mock occurs, but it is unexpected due to some reasons
1497-
// (e.g. we do not have mock framework installed),
1498-
// we can only generate a test that uses null value for mocked object
1499-
queuedSymbolicStateUpdates += nullEqualityConstraint.asHardConstraint()
1500-
1501-
mockedObjectInfo.value
1502-
}
1503-
}
1489+
if (allowsConcreteTypes && typeStorage.possibleConcreteTypes.any()) {
1490+
// If we have this$0 with UtArrayList type, we have to create such instance.
1491+
// We should create an object with typeStorage of all possible real types and concrete implementation
1492+
// Otherwise we'd have either a wrong type in the resolver, or missing method like 'preconditionCheck'.
1493+
val concreteImplementation = wrapperToClass[type]?.first()?.let { wrapper(it, addr) }?.concrete
1494+
val isMockConstraint = mkEq(typeRegistry.isMock(addr), UtFalse)
15041495

1505-
if (mockedObjectInfo is UnexpectedMock) {
1506-
return mockedObject
1507-
}
1496+
queuedSymbolicStateUpdates += typeHardConstraint
1497+
queuedSymbolicStateUpdates += mkOr(isMockConstraint, nullEqualityConstraint).asHardConstraint()
15081498

1509-
queuedSymbolicStateUpdates += MemoryUpdate(mockInfos = persistentListOf(MockInfoEnriched(mockInfo)))
1499+
return ObjectValue(typeStorage, addr, concreteImplementation)
1500+
}
15101501

1511-
// add typeConstraint for mocked object. It's a declared type of the object.
1512-
val typeConstraint = typeRegistry.typeConstraint(addr, mockedObject.typeStorage).all()
1513-
val isMockConstraint = mkEq(typeRegistry.isMock(mockedObject.addr), UtTrue)
1502+
requireNotNull(mockInfoGenerator) {
1503+
"An object with $addr and $type doesn't have concrete possible types," +
1504+
"but there is no mock info generator provided to construct a mock value."
1505+
}
15141506

1515-
queuedSymbolicStateUpdates += typeConstraint.asHardConstraint()
1516-
queuedSymbolicStateUpdates += mkOr(isMockConstraint, nullEqualityConstraint).asHardConstraint()
1507+
val mockInfo = mockInfoGenerator.generate(addr)
1508+
val mockedObjectInfo = mocker.forceMock(type, mockInfoGenerator.generate(addr))
1509+
1510+
val mockedObject: ObjectValue = when (mockedObjectInfo) {
1511+
is NoMock -> error("Value must be mocked after the force mock")
1512+
is ExpectedMock -> mockedObjectInfo.value
1513+
is UnexpectedMock -> {
1514+
// if mock occurs, but it is unexpected due to some reasons
1515+
// (e.g. we do not have mock framework installed),
1516+
// we can only generate a test that uses null value for mocked object
1517+
queuedSymbolicStateUpdates += nullEqualityConstraint.asHardConstraint()
15171518

1519+
mockedObjectInfo.value
1520+
}
1521+
}
1522+
1523+
if (mockedObjectInfo is UnexpectedMock) {
15181524
return mockedObject
15191525
}
15201526

1521-
// If we have this$0 with UtArrayList type, we have to create such instance.
1522-
// We should create an object with typeStorage of all possible real types and concrete implementation
1523-
// Otherwise we'd have either a wrong type in the resolver, or missing method like 'preconditionCheck'.
1524-
val concreteImplementation = wrapperToClass[type]?.first()?.let { wrapper(it, addr) }?.concrete
1525-
val isMockConstraint = mkEq(typeRegistry.isMock(addr), UtFalse)
1527+
queuedSymbolicStateUpdates += MemoryUpdate(mockInfos = persistentListOf(MockInfoEnriched(mockInfo)))
15261528

1527-
queuedSymbolicStateUpdates += typeHardConstraint
1529+
// add typeConstraint for mocked object. It's a declared type of the object.
1530+
val typeConstraint = typeRegistry.typeConstraint(addr, mockedObject.typeStorage).all()
1531+
val isMockConstraint = mkEq(typeRegistry.isMock(mockedObject.addr), UtTrue)
1532+
1533+
queuedSymbolicStateUpdates += typeConstraint.asHardConstraint()
15281534
queuedSymbolicStateUpdates += mkOr(isMockConstraint, nullEqualityConstraint).asHardConstraint()
15291535

1530-
return ObjectValue(typeStorage, addr, concreteImplementation)
1536+
return mockedObject
15311537
}
15321538

1539+
15331540
private fun TraversalContext.resolveConstant(constant: Constant): SymbolicValue =
15341541
when (constant) {
15351542
is IntConstant -> constant.value.toPrimitiveValue()

utbot-framework/src/main/kotlin/org/utbot/engine/UtBotSymbolicEngine.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ class UtBotSymbolicEngine(
105105
dependencyPaths: String,
106106
val mockStrategy: MockStrategy = NO_MOCKS,
107107
chosenClassesToMockAlways: Set<ClassId>,
108-
applicationContext: StandardApplicationContext,
108+
applicationContext: ApplicationContext,
109109
private val solverTimeoutInMillis: Int = checkSolverTimeoutMillis
110110
) : UtContextInitializer() {
111111
private val graph = methodUnderTest.sootMethod.jimpleBody().apply {

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/Domain.kt

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -711,3 +711,32 @@ enum class ParametrizedTestSource(
711711
override val allItems: List<ParametrizedTestSource> = values().toList()
712712
}
713713
}
714+
715+
enum class ApplicationType {
716+
/**
717+
* Standard JVM application without DI frameworks.
718+
*/
719+
PURE_JVM,
720+
721+
/**
722+
* Spring or Spring Boot application.
723+
*/
724+
SPRING_APPLICATION,
725+
}
726+
727+
enum class TypeReplacementApproach {
728+
/**
729+
* Do not replace interfaces and abstract classes with concrete implementors.
730+
* Use mocking instead of it.
731+
*/
732+
DO_NOT_REPLACE,
733+
734+
/**
735+
* Try to replace interfaces and abstract classes with concrete implementors
736+
* obtained from bean definitions.
737+
* If it is impossible, use mocking.
738+
*
739+
* Currently used in Spring applications only.
740+
*/
741+
REPLACE_IF_POSSIBLE,
742+
}

utbot-framework/src/main/kotlin/org/utbot/framework/plugin/api/TestCaseGenerator.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ open class TestCaseGenerator(
6161
val engineActions: MutableList<(UtBotSymbolicEngine) -> Unit> = mutableListOf(),
6262
val isCanceled: () -> Boolean = { false },
6363
val forceSootReload: Boolean = true,
64-
val applicationContext: StandardApplicationContext = StandardApplicationContext(),
64+
val applicationContext: ApplicationContext = ApplicationContext(),
6565
) {
6666
private val logger: KLogger = KotlinLogging.logger {}
6767
private val timeoutLogger: KLogger = KotlinLogging.logger(logger.name + ".timeout")
@@ -257,7 +257,7 @@ open class TestCaseGenerator(
257257
method: ExecutableId,
258258
mockStrategyApi: MockStrategyApi,
259259
chosenClassesToMockAlways: Set<ClassId>,
260-
applicationContext: StandardApplicationContext,
260+
applicationContext: ApplicationContext,
261261
executionTimeEstimator: ExecutionTimeEstimator
262262
): UtBotSymbolicEngine {
263263
logger.debug("Starting symbolic execution for $method --$mockStrategyApi--")

utbot-framework/src/main/kotlin/org/utbot/framework/process/EngineProcessMain.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ private fun EngineProcessModel.setup(kryoHelper: KryoHelper, watchdog: IdleWatch
8181
watchdog.measureTimeForActiveCall(createTestGenerator, "Creating Test Generator") { params ->
8282
AnalyticsConfigureUtil.configureML()
8383
Instrumenter.adapter = RdInstrumenter(realProtocol.rdInstrumenterAdapter)
84-
val applicationContext: StandardApplicationContext = kryoHelper.readObject(params.applicationContext)
84+
val applicationContext: ApplicationContext = kryoHelper.readObject(params.applicationContext)
8585

8686
testGenerator = TestCaseGenerator(buildDirs = params.buildDir.map { Paths.get(it) },
8787
classpath = params.classpath,
@@ -99,7 +99,7 @@ private fun EngineProcessModel.setup(kryoHelper: KryoHelper, watchdog: IdleWatch
9999
logger.debug().measureTime({ "starting generation for ${methods.size} methods, starting with ${methods.first()}" }) {
100100
val generateFlow = when (testGenerator.applicationContext) {
101101
is SpringApplicationContext -> defaultSpringFlow(params.generationTimeout)
102-
is StandardApplicationContext -> testFlow {
102+
is ApplicationContext -> testFlow {
103103
generationTimeout = params.generationTimeout
104104
isSymbolicEngineEnabled = params.isSymbolicEngineEnabled
105105
isFuzzingEnabled = params.isFuzzingEnabled

utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/UtTestsDialogProcessor.kt

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,12 @@ import org.utbot.framework.CancellationStrategyType.CANCEL_EVERYTHING
2525
import org.utbot.framework.CancellationStrategyType.NONE
2626
import org.utbot.framework.CancellationStrategyType.SAVE_PROCESSED_RESULTS
2727
import org.utbot.framework.UtSettings
28+
import org.utbot.framework.codegen.domain.ApplicationType
29+
import org.utbot.framework.codegen.domain.TypeReplacementApproach
2830
import org.utbot.framework.plugin.api.ClassId
29-
import org.utbot.framework.plugin.api.StandardApplicationContext
31+
import org.utbot.framework.plugin.api.ApplicationContext
3032
import org.utbot.framework.plugin.api.JavaDocCommentStyle
33+
import org.utbot.framework.plugin.api.SpringApplicationContext
3134
import org.utbot.framework.plugin.api.util.LockFile
3235
import org.utbot.framework.plugin.api.util.withStaticsSubstitutionRequired
3336
import org.utbot.framework.plugin.services.JdkInfoService
@@ -170,12 +173,26 @@ object UtTestsDialogProcessor {
170173
}
171174
}
172175

173-
val applicationContext = StandardApplicationContext(
174-
model.mockFramework.isInstalled,
175-
model.staticsMocking.isConfigured,
176-
)
177-
// TODO: obtain bean definitions and other info from `utbot-spring-analyzer`
178-
//SpringApplicationContext(beanQualifiedNames = emptyList())
176+
val mockFrameworkInstalled = model.mockFramework.isInstalled
177+
val staticMockingConfigured = model.staticsMocking.isConfigured
178+
179+
val applicationContext = when (model.applicationType) {
180+
ApplicationType.PURE_JVM -> ApplicationContext(mockFrameworkInstalled, staticMockingConfigured)
181+
ApplicationType.SPRING_APPLICATION -> {
182+
val shouldUseImplementors = when (model.typeReplacementApproach) {
183+
TypeReplacementApproach.DO_NOT_REPLACE -> false
184+
TypeReplacementApproach.REPLACE_IF_POSSIBLE -> true
185+
}
186+
187+
SpringApplicationContext(
188+
mockFrameworkInstalled,
189+
staticMockingConfigured,
190+
// TODO: obtain bean definitions and other info from `utbot-spring-analyzer`
191+
beanQualifiedNames = emptyList(),
192+
shouldUseImplementors = shouldUseImplementors,
193+
)
194+
}
195+
}
179196

180197
val process = EngineProcess.createBlocking(project, classNameToPath)
181198

utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/models/GenerateTestsModel.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import com.intellij.refactoring.util.classMembers.MemberInfo
1818
import org.jetbrains.kotlin.psi.KtFile
1919
import org.utbot.framework.SummariesGenerationType
2020
import org.utbot.framework.UtSettings
21+
import org.utbot.framework.codegen.domain.ApplicationType
22+
import org.utbot.framework.codegen.domain.TypeReplacementApproach
2123
import org.utbot.framework.plugin.api.JavaDocCommentStyle
2224
import org.utbot.framework.util.ConflictTriggers
2325
import org.utbot.intellij.plugin.settings.Settings
@@ -41,6 +43,8 @@ class GenerateTestsModel(
4143
override var sourceRootHistory = project.service<Settings>().sourceRootHistory
4244
override var codegenLanguage = project.service<Settings>().codegenLanguage
4345

46+
lateinit var applicationType: ApplicationType
47+
4448
lateinit var testFramework: TestFramework
4549
lateinit var mockStrategy: MockStrategyApi
4650
lateinit var mockFramework: MockFramework
@@ -53,6 +57,8 @@ class GenerateTestsModel(
5357
lateinit var chosenClassesToMockAlways: Set<ClassId>
5458
lateinit var commentStyle: JavaDocCommentStyle
5559

60+
lateinit var typeReplacementApproach: TypeReplacementApproach
61+
5662
val conflictTriggers: ConflictTriggers = ConflictTriggers()
5763

5864
var runGeneratedTestsWithCoverage : Boolean = false

utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/process/EngineProcess.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ class EngineProcess private constructor(val project: Project, private val classN
183183
classPath: String?,
184184
dependencyPaths: String,
185185
jdkInfo: JdkInfo,
186-
applicationContext: StandardApplicationContext,
186+
applicationContext: ApplicationContext,
187187
isCancelled: (Unit) -> Boolean
188188
) {
189189
assertReadAccessNotAllowed()

utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/GenerateTestsDialogWindow.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,12 +97,14 @@ import org.jetbrains.concurrency.Promise
9797
import org.jetbrains.concurrency.thenRun
9898
import org.utbot.common.PathUtil.toPath
9999
import org.utbot.framework.UtSettings
100+
import org.utbot.framework.codegen.domain.ApplicationType
100101
import org.utbot.framework.codegen.domain.ForceStaticMocking
101102
import org.utbot.framework.codegen.domain.Junit4
102103
import org.utbot.framework.codegen.domain.Junit5
103104
import org.utbot.framework.codegen.domain.MockitoStaticMocking
104105
import org.utbot.framework.codegen.domain.NoStaticMocking
105106
import org.utbot.framework.codegen.domain.ParametrizedTestSource
107+
import org.utbot.framework.codegen.domain.TypeReplacementApproach
106108
import org.utbot.framework.codegen.domain.StaticsMocking
107109
import org.utbot.framework.codegen.domain.TestFramework
108110
import org.utbot.framework.codegen.domain.TestNg
@@ -522,6 +524,10 @@ class GenerateTestsDialogWindow(val model: GenerateTestsModel) : DialogWrapper(m
522524
model.timeout = TimeUnit.SECONDS.toMillis(timeoutSpinner.number.toLong())
523525
model.testSourceRoot?.apply { model.updateSourceRootHistory(this.toNioPath().toString()) }
524526

527+
//TODO: obtain this values from UI controls https://github.com/UnitTestBot/UTBotJava/issues/1929
528+
model.applicationType = ApplicationType.PURE_JVM
529+
model.typeReplacementApproach = TypeReplacementApproach.DO_NOT_REPLACE
530+
525531
val settings = model.project.service<Settings>()
526532
with(settings) {
527533
model.runtimeExceptionTestsBehaviour = runtimeExceptionTestsBehaviour

0 commit comments

Comments
 (0)