From 9ff64a0c50609d736af966d73d82d3f7b41a33a8 Mon Sep 17 00:00:00 2001 From: Egor Kulikov Date: Fri, 3 Mar 2023 13:02:54 +0300 Subject: [PATCH 01/12] Listen that mock occured in forceMock method --- utbot-framework/src/main/kotlin/org/utbot/engine/Mocks.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 231d31c187..b032adb3da 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Mocks.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Mocks.kt @@ -151,7 +151,10 @@ class Mocker( * Creates mocked instance of the [type] using mock info. Unlike to [mock], it does not * check anything and always returns the constructed mock. */ - fun forceMock(type: RefType, mockInfo: UtMockInfo): ObjectValue = createMockObject(type, mockInfo) + fun forceMock(type: RefType, mockInfo: UtMockInfo): ObjectValue { + mockListenerController?.onShouldMock(strategy, mockInfo) + return createMockObject(type, mockInfo) + } /** * Checks if Engine should mock objects of particular type with current mock strategy and mock type. From dbc3312dd5733a6be13ed81633a8e9597da1dae9 Mon Sep 17 00:00:00 2001 From: Egor Kulikov Date: Fri, 3 Mar 2023 18:55:46 +0300 Subject: [PATCH 02/12] An attempt to unsat branched with unexpected mocking --- .../src/main/kotlin/org/utbot/engine/Mocks.kt | 33 ++++++++++++++++--- .../main/kotlin/org/utbot/engine/Traverser.kt | 19 +++++++++-- 2 files changed, 45 insertions(+), 7 deletions(-) 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 b032adb3da..86b2d61cb5 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Mocks.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Mocks.kt @@ -128,6 +128,23 @@ data class UtStaticMethodMockInfo( val methodId: MethodId ) : UtMockInfo(methodId.classId, addr) +/** + * A wrapper for [ObjectValue] to store additional onfo. + */ +sealed class MockedObjectInfo(val value: ObjectValue?) + +object NoMock: MockedObjectInfo(value = null) +class ExpectedMock(value: ObjectValue): MockedObjectInfo(value) + +/** + * Represents a mock that occurs when it is not recommended + * E.g. mock strategy recommends do not mock + */ +class UnexpectedMock(value: ObjectValue): MockedObjectInfo(value) + +fun ObjectValue?.construct(mockDesired: Boolean): MockedObjectInfo = + this?.let { if (mockDesired) ExpectedMock(it) else UnexpectedMock(it) } ?: NoMock + /** * Service to mock things. Knows mock strategy, class under test and class hierarchy. */ @@ -138,22 +155,28 @@ class Mocker( chosenClassesToMockAlways: Set, internal val mockListenerController: MockListenerController? = null, ) { + private val mocksDesired: Boolean = strategy != MockStrategy.NO_MOCKS + /** * Creates mocked instance of the [type] using mock info if it should be mocked by the mocker, * otherwise returns null. * * @see shouldMock */ - fun mock(type: RefType, mockInfo: UtMockInfo): ObjectValue? = - if (shouldMock(type, mockInfo)) createMockObject(type, mockInfo) else null + fun mock(type: RefType, mockInfo: UtMockInfo): MockedObjectInfo { + val objectValue = if (shouldMock(type, mockInfo)) createMockObject(type, mockInfo) else null + return objectValue.construct(mocksDesired) + } /** * Creates mocked instance of the [type] using mock info. Unlike to [mock], it does not * check anything and always returns the constructed mock. */ - fun forceMock(type: RefType, mockInfo: UtMockInfo): ObjectValue { + fun forceMock(type: RefType, mockInfo: UtMockInfo): MockedObjectInfo { mockListenerController?.onShouldMock(strategy, mockInfo) - return createMockObject(type, mockInfo) + + val objectValue = createMockObject(type, mockInfo) + return objectValue.construct(mocksDesired) } /** @@ -180,6 +203,8 @@ class Mocker( } } + val areMocksAllowed: Boolean = strategy == MockStrategy.NO_MOCKS + private fun checkIfShouldMock( type: RefType, mockInfo: UtMockInfo 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 5f8ce0d1f3..626fa125e2 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt @@ -1383,7 +1383,13 @@ class Traverser( queuedSymbolicStateUpdates += MemoryUpdate(addrToMockInfo = persistentHashMapOf(addr to mockInfo)) - val mockedObject = mocker.mock(type, mockInfo) + val mockedObjectInfo = mocker.mock(type, mockInfo) + + val mockedObject = mockedObjectInfo.value + if (mockedObjectInfo is UnexpectedMock) { + queuedSymbolicStateUpdates += UtFalse.asHardConstraint() + } + if (mockedObject != null) { queuedSymbolicStateUpdates += MemoryUpdate(mockInfos = persistentListOf(MockInfoEnriched(mockInfo))) @@ -1470,7 +1476,12 @@ class Traverser( } val mockInfo = mockInfoGenerator.generate(addr) - val mockedObject = mocker.forceMock(type, mockInfoGenerator.generate(addr)) + val mockedObjectInfo = mocker.forceMock(type, mockInfoGenerator.generate(addr)) + + val mockedObject = mockedObjectInfo.value ?: error("Mocked value cannot be null after force mock") + if (mockedObjectInfo is UnexpectedMock) { + queuedSymbolicStateUpdates += UtFalse.asHardConstraint() + } queuedSymbolicStateUpdates += MemoryUpdate(mockInfos = persistentListOf(MockInfoEnriched(mockInfo))) @@ -2684,7 +2695,9 @@ class Traverser( } else { it.copyWithClassId(classId = implementationClass.id) } - mocker.mock(implementationClass, updatedMockInfo) + + val mockedObjectInfo = mocker.mock(implementationClass, updatedMockInfo) + mockedObjectInfo.value } if (mockedObject == null) { From 204f8bd769cbadf36e0b0385bb610c62717b9739 Mon Sep 17 00:00:00 2001 From: Egor Kulikov Date: Fri, 3 Mar 2023 19:29:04 +0300 Subject: [PATCH 03/12] Further improvements of traverser --- .../src/main/kotlin/org/utbot/engine/Mocks.kt | 2 - .../main/kotlin/org/utbot/engine/Traverser.kt | 47 ++++++++++--------- 2 files changed, 25 insertions(+), 24 deletions(-) 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 86b2d61cb5..5cb0117720 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Mocks.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Mocks.kt @@ -203,8 +203,6 @@ class Mocker( } } - val areMocksAllowed: Boolean = strategy == MockStrategy.NO_MOCKS - private fun checkIfShouldMock( type: RefType, mockInfo: UtMockInfo 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 626fa125e2..50ea215fc1 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt @@ -2683,10 +2683,10 @@ class Traverser( } ?: findMethodInvocationTargets(types, methodSubSignature) return methodInvocationTargets - .map { (method, implementationClass, possibleTypes) -> + .mapNotNull { (method, implementationClass, possibleTypes) -> val typeStorage = typeResolver.constructTypeStorage(implementationClass, possibleTypes) val mockInfo = memory.mockInfoByAddr(instance.addr) - val mockedObject = mockInfo?.let { + val mockedObjectInfo = mockInfo?.let { // TODO rewrite to fix JIRA:1611 val type = Scene.v().getSootClass(mockInfo.classId.name).type val ancestorTypes = typeResolver.findOrConstructAncestorsIncludingTypes(type) @@ -2696,29 +2696,32 @@ class Traverser( it.copyWithClassId(classId = implementationClass.id) } - val mockedObjectInfo = mocker.mock(implementationClass, updatedMockInfo) - mockedObjectInfo.value - } - - if (mockedObject == null) { - // Above we might get implementationClass that has to be substituted. - // For example, for a call "Collection.size()" such classes will be produced. - val wrapperOrInstance = wrapper(implementationClass, instance.addr) - ?: instance.copy(typeStorage = typeStorage) - - val typeConstraint = typeRegistry.typeConstraint(instance.addr, wrapperOrInstance.typeStorage) - val constraints = setOf(typeConstraint.isOrNullConstraint()) + mocker.mock(implementationClass, updatedMockInfo) + } ?: NoMock - // TODO add memory updated for types JIRA:1523 + when (mockedObjectInfo) { + is NoMock -> { + // Above we might get implementationClass that has to be substituted. + // For example, for a call "Collection.size()" such classes will be produced. + val wrapperOrInstance = wrapper(implementationClass, instance.addr) + ?: instance.copy(typeStorage = typeStorage) - InvocationTarget(wrapperOrInstance, method, constraints) - } else { - val typeConstraint = typeRegistry.typeConstraint(mockedObject.addr, mockedObject.typeStorage) - val constraints = setOf(typeConstraint.isOrNullConstraint()) + val typeConstraint = typeRegistry.typeConstraint(instance.addr, wrapperOrInstance.typeStorage) + val constraints = setOf(typeConstraint.isOrNullConstraint()) - // TODO add memory updated for types JIRA:1523 - // TODO isMock???? - InvocationTarget(mockedObject, method, constraints) + // TODO add memory updated for types JIRA:1523 + InvocationTarget(wrapperOrInstance, method, constraints) + } + is ExpectedMock -> { + val mockedObject = mockedObjectInfo.value!! + val typeConstraint = typeRegistry.typeConstraint(mockedObject.addr, mockedObject.typeStorage) + val constraints = setOf(typeConstraint.isOrNullConstraint()) + + // TODO add memory updated for types JIRA:1523 + // TODO isMock???? + InvocationTarget(mockedObject, method, constraints) + } + is UnexpectedMock -> null } } } From 69acad42914fb6a36dd0fd39b7147310bd885696 Mon Sep 17 00:00:00 2001 From: Egor Kulikov Date: Mon, 6 Mar 2023 11:50:20 +0300 Subject: [PATCH 04/12] Correct tests in CommonMockExampleTest, do related fixes --- .../examples/mock/CommonMocksExampleTest.kt | 17 +++++++++++++++-- .../main/kotlin/org/utbot/engine/Traverser.kt | 9 ++++----- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/utbot-framework-test/src/test/kotlin/org/utbot/examples/mock/CommonMocksExampleTest.kt b/utbot-framework-test/src/test/kotlin/org/utbot/examples/mock/CommonMocksExampleTest.kt index 9dc536f2e6..3b80d7801a 100644 --- a/utbot-framework-test/src/test/kotlin/org/utbot/examples/mock/CommonMocksExampleTest.kt +++ b/utbot-framework-test/src/test/kotlin/org/utbot/examples/mock/CommonMocksExampleTest.kt @@ -7,14 +7,27 @@ import org.utbot.testing.DoNotCalculate import org.utbot.testing.UtValueTestCaseChecker internal class CommonMocksExampleTest: UtValueTestCaseChecker(testClass = CommonMocksExample::class) { + @Test - fun testMockInterfaceWithoutImplementors() { + fun testMockInterfaceWithoutImplementorsWithNoMocksStrategy() { + checkMocks( + CommonMocksExample::mockInterfaceWithoutImplementors, + eq(1), + { v, mocks, _ -> v == null && mocks.isEmpty() }, + coverage = DoNotCalculate, + mockStrategy = MockStrategyApi.NO_MOCKS, + ) + } + + @Test + fun testMockInterfaceWithoutImplementorsWithMockingStrategy() { checkMocks( CommonMocksExample::mockInterfaceWithoutImplementors, eq(2), { v, mocks, _ -> v == null && mocks.isEmpty() }, { _, mocks, _ -> mocks.singleOrNull() != null }, - coverage = DoNotCalculate + coverage = DoNotCalculate, + mockStrategy = MockStrategyApi.OTHER_CLASSES, ) } 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 50ea215fc1..232a551dd4 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt @@ -277,8 +277,6 @@ class Traverser( internal val objectCounter = ObjectCounter(TypeRegistry.objectCounterInitialValue) - - private fun findNewAddr(insideStaticInitializer: Boolean): UtAddrExpression { val newAddr = objectCounter.createNewAddr() // return negative address for objects created inside static initializer @@ -1387,7 +1385,7 @@ class Traverser( val mockedObject = mockedObjectInfo.value if (mockedObjectInfo is UnexpectedMock) { - queuedSymbolicStateUpdates += UtFalse.asHardConstraint() + error("Wrong mocker configuration, it has NO_MOCK stategy, but decided to mock $type object") } @@ -1480,7 +1478,8 @@ class Traverser( val mockedObject = mockedObjectInfo.value ?: error("Mocked value cannot be null after force mock") if (mockedObjectInfo is UnexpectedMock) { - queuedSymbolicStateUpdates += UtFalse.asHardConstraint() + queuedSymbolicStateUpdates += nullEqualityConstraint.asHardConstraint() + return mockedObject } queuedSymbolicStateUpdates += MemoryUpdate(mockInfos = persistentListOf(MockInfoEnriched(mockInfo))) @@ -2721,7 +2720,7 @@ class Traverser( // TODO isMock???? InvocationTarget(mockedObject, method, constraints) } - is UnexpectedMock -> null + is UnexpectedMock -> unreachableBranch("If it ever happens, it should be investigated") } } } From b99849fc2792bc0b8130539f7444e5c5897477d3 Mon Sep 17 00:00:00 2001 From: Egor Kulikov Date: Mon, 6 Mar 2023 12:32:35 +0300 Subject: [PATCH 05/12] All tests pass somehow, pray for them --- .../src/main/kotlin/org/utbot/engine/Mocks.kt | 15 ++++++++++----- .../src/main/kotlin/org/utbot/engine/Traverser.kt | 2 +- 2 files changed, 11 insertions(+), 6 deletions(-) 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 5cb0117720..d14b345597 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Mocks.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Mocks.kt @@ -134,6 +134,11 @@ data class UtStaticMethodMockInfo( sealed class MockedObjectInfo(val value: ObjectValue?) object NoMock: MockedObjectInfo(value = null) + +/** + * Represents a mock that occurs when mock strategy allows it + * or when an object type is in a set that requires force mocking. + */ class ExpectedMock(value: ObjectValue): MockedObjectInfo(value) /** @@ -142,9 +147,6 @@ class ExpectedMock(value: ObjectValue): MockedObjectInfo(value) */ class UnexpectedMock(value: ObjectValue): MockedObjectInfo(value) -fun ObjectValue?.construct(mockDesired: Boolean): MockedObjectInfo = - this?.let { if (mockDesired) ExpectedMock(it) else UnexpectedMock(it) } ?: NoMock - /** * Service to mock things. Knows mock strategy, class under test and class hierarchy. */ @@ -157,6 +159,9 @@ class Mocker( ) { private val mocksDesired: Boolean = strategy != MockStrategy.NO_MOCKS + fun construct(value: ObjectValue?): MockedObjectInfo = + value?.let { if (mocksDesired || mockAlways(it.type) ) ExpectedMock(it) else UnexpectedMock(it) } ?: NoMock + /** * Creates mocked instance of the [type] using mock info if it should be mocked by the mocker, * otherwise returns null. @@ -165,7 +170,7 @@ class Mocker( */ fun mock(type: RefType, mockInfo: UtMockInfo): MockedObjectInfo { val objectValue = if (shouldMock(type, mockInfo)) createMockObject(type, mockInfo) else null - return objectValue.construct(mocksDesired) + return construct(objectValue) } /** @@ -176,7 +181,7 @@ class Mocker( mockListenerController?.onShouldMock(strategy, mockInfo) val objectValue = createMockObject(type, mockInfo) - return objectValue.construct(mocksDesired) + return construct(objectValue) } /** 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 232a551dd4..df538b4202 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt @@ -1385,7 +1385,7 @@ class Traverser( val mockedObject = mockedObjectInfo.value if (mockedObjectInfo is UnexpectedMock) { - error("Wrong mocker configuration, it has NO_MOCK stategy, but decided to mock $type object") + error("Wrong mocker configuration, it decided to mock $type object, although it is unexpected") } From e2169dc97692172ba2111aa2dcf0c38b28502ffb Mon Sep 17 00:00:00 2001 From: Egor Kulikov Date: Mon, 6 Mar 2023 12:57:27 +0300 Subject: [PATCH 06/12] Some style improvements to job cancellation --- .../engine/util/mockListeners/ForceMockListener.kt | 12 ++++++++---- .../util/mockListeners/ForceStaticMockListener.kt | 12 ++++++++---- .../utbot/framework/plugin/api/TestCaseGenerator.kt | 4 ++-- .../org/utbot/framework/process/EngineProcessMain.kt | 4 ++-- .../utbot/testing/TestSpecificTestCaseGenerator.kt | 4 ++-- 5 files changed, 22 insertions(+), 14 deletions(-) diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/util/mockListeners/ForceMockListener.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/util/mockListeners/ForceMockListener.kt index f07f906259..7ece9a40d7 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/util/mockListeners/ForceMockListener.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/util/mockListeners/ForceMockListener.kt @@ -12,17 +12,21 @@ import org.utbot.framework.util.ConflictTriggers * * Supposed to be created only if Mockito is not installed. */ -class ForceMockListener private constructor(triggers: ConflictTriggers, private val cancelJob: Boolean): MockListener(triggers) { +class ForceMockListener private constructor(triggers: ConflictTriggers, private val shouldCancelJob: Boolean): MockListener(triggers) { override fun onShouldMock(controller: EngineController, strategy: MockStrategy, mockInfo: UtMockInfo) { // If force mocking happened -- сancel engine job - if (cancelJob) controller.job?.cancel(ForceMockCancellationException()) + if (shouldCancelJob) controller.job?.cancel(ForceMockCancellationException()) triggers[Conflict.ForceMockHappened] = true } companion object { - fun create(testCaseGenerator: TestCaseGenerator, conflictTriggers: ConflictTriggers, cancelJob: Boolean = true) : ForceMockListener { - val listener = ForceMockListener(conflictTriggers, cancelJob) + fun create( + testCaseGenerator: TestCaseGenerator, + conflictTriggers: ConflictTriggers, + shouldCancelJob: Boolean = false, + ) : ForceMockListener { + val listener = ForceMockListener(conflictTriggers, shouldCancelJob) testCaseGenerator.engineActions.add { engine -> engine.attachMockListener(listener) } return listener diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/util/mockListeners/ForceStaticMockListener.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/util/mockListeners/ForceStaticMockListener.kt index 1e0ec17016..ba45a07c86 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/util/mockListeners/ForceStaticMockListener.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/util/mockListeners/ForceStaticMockListener.kt @@ -16,21 +16,25 @@ import org.utbot.framework.util.ConflictTriggers * * Supposed to be created only if Mockito inline is not installed. */ -class ForceStaticMockListener private constructor(triggers: ConflictTriggers, private val cancelJob: Boolean): MockListener(triggers) { +class ForceStaticMockListener private constructor(triggers: ConflictTriggers, private val shouldCancelJob: Boolean): MockListener(triggers) { override fun onShouldMock(controller: EngineController, strategy: MockStrategy, mockInfo: UtMockInfo) { if (mockInfo is UtNewInstanceMockInfo || mockInfo is UtStaticMethodMockInfo || mockInfo is UtStaticObjectMockInfo) { // If force static mocking happened -- сancel engine job - if (cancelJob) controller.job?.cancel(ForceStaticMockCancellationException()) + if (shouldCancelJob) controller.job?.cancel(ForceStaticMockCancellationException()) triggers[Conflict.ForceStaticMockHappened] = true } } companion object { - fun create(testCaseGenerator: TestCaseGenerator, conflictTriggers: ConflictTriggers, cancelJob: Boolean = true) : ForceStaticMockListener { - val listener = ForceStaticMockListener(conflictTriggers, cancelJob) + fun create( + testCaseGenerator: TestCaseGenerator, + conflictTriggers: ConflictTriggers, + shouldCancelJob: Boolean = false, + ) : ForceStaticMockListener { + val listener = ForceStaticMockListener(conflictTriggers, shouldCancelJob) testCaseGenerator.engineActions.add { engine -> engine.attachMockListener(listener) } return listener 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 3977e178c7..0a0eb8cae6 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 @@ -149,8 +149,8 @@ open class TestCaseGenerator( val method2errors = methods.associateWith { mutableMapOf() } val conflictTriggers = ConflictTriggers() - val forceMockListener = ForceMockListener.create(this, conflictTriggers, cancelJob = false) - val forceStaticMockListener = ForceStaticMockListener.create(this, conflictTriggers, cancelJob = false) + val forceMockListener = ForceMockListener.create(this, conflictTriggers) + val forceStaticMockListener = ForceStaticMockListener.create(this, conflictTriggers) runIgnoringCancellationException { runBlockingWithCancellationPredicate(isCanceled) { 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 3ef77f5a65..f00abfdd34 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 @@ -102,11 +102,11 @@ private fun EngineProcessModel.setup(kryoHelper: KryoHelper, watchdog: IdleWatch val mockFrameworkInstalled = params.mockInstalled val conflictTriggers = ConflictTriggers(kryoHelper.readObject(params.conflictTriggers)) if (!mockFrameworkInstalled) { - ForceMockListener.create(testGenerator, conflictTriggers, cancelJob = true) + ForceMockListener.create(testGenerator, conflictTriggers, shouldCancelJob = true) } val staticsMockingConfigured = params.staticsMockingIsConfigureda if (!staticsMockingConfigured) { - ForceStaticMockListener.create(testGenerator, conflictTriggers, cancelJob = true) + ForceStaticMockListener.create(testGenerator, conflictTriggers, shouldCancelJob = true) } val generateFlow = when (testGenerator.applicationContext) { diff --git a/utbot-testing/src/main/kotlin/org/utbot/testing/TestSpecificTestCaseGenerator.kt b/utbot-testing/src/main/kotlin/org/utbot/testing/TestSpecificTestCaseGenerator.kt index ea3c761f54..71d9e2d233 100644 --- a/utbot-testing/src/main/kotlin/org/utbot/testing/TestSpecificTestCaseGenerator.kt +++ b/utbot-testing/src/main/kotlin/org/utbot/testing/TestSpecificTestCaseGenerator.kt @@ -57,8 +57,8 @@ class TestSpecificTestCaseGenerator( val mockAlwaysDefaults = Mocker.javaDefaultClasses.mapTo(mutableSetOf()) { it.id } val defaultTimeEstimator = ExecutionTimeEstimator(UtSettings.utBotGenerationTimeoutInMillis, 1) - val forceMockListener = ForceMockListener.create(this, conflictTriggers, cancelJob = false) - val forceStaticMockListener = ForceStaticMockListener.create(this, conflictTriggers, cancelJob = false) + val forceMockListener = ForceMockListener.create(this, conflictTriggers) + val forceStaticMockListener = ForceStaticMockListener.create(this, conflictTriggers) runBlocking { val controller = EngineController() From bc49207199ff9ed2d5bb3bde0a2fb074cf501090 Mon Sep 17 00:00:00 2001 From: Egor Kulikov Date: Mon, 6 Mar 2023 15:10:05 +0300 Subject: [PATCH 07/12] Little corrections --- utbot-framework/src/main/kotlin/org/utbot/engine/Mocks.kt | 4 +++- .../utbot/intellij/plugin/generator/UtTestsDialogProcessor.kt | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) 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 d14b345597..ce6e37d16d 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Mocks.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Mocks.kt @@ -160,7 +160,9 @@ class Mocker( private val mocksDesired: Boolean = strategy != MockStrategy.NO_MOCKS fun construct(value: ObjectValue?): MockedObjectInfo = - value?.let { if (mocksDesired || mockAlways(it.type) ) ExpectedMock(it) else UnexpectedMock(it) } ?: NoMock + value + ?.let { if (mocksDesired || mockAlways(it.type)) ExpectedMock(it) else UnexpectedMock(it) } + ?: NoMock /** * Creates mocked instance of the [type] using mock info if it should be mocked by the mocker, 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 001e206784..c43e0082b5 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 @@ -249,7 +249,7 @@ object UtTestsDialogProcessor { .executeSynchronously() withStaticsSubstitutionRequired(true) { - val mockFrameworkInstalled = model.mockFramework?.isInstalled ?: true + val mockFrameworkInstalled = model.mockFramework.isInstalled val startTime = System.currentTimeMillis() val timerHandler = From db47614ce342b4c87bf495e6dbbc3a2328a0895e Mon Sep 17 00:00:00 2001 From: Egor Kulikov Date: Mon, 6 Mar 2023 16:49:05 +0300 Subject: [PATCH 08/12] Try to remove job cancellation from mock listeners --- .../org/utbot/framework/plugin/api/Api.kt | 18 ++++---- .../src/main/kotlin/org/utbot/engine/Mocks.kt | 30 +++++++++--- .../main/kotlin/org/utbot/engine/Traverser.kt | 3 +- .../org/utbot/engine/UtBotSymbolicEngine.kt | 5 +- .../util/mockListeners/ForceMockListener.kt | 14 ++---- .../mockListeners/ForceStaticMockListener.kt | 18 ++------ .../framework/plugin/api/TestCaseGenerator.kt | 6 +-- .../framework/process/EngineProcessMain.kt | 15 +----- .../generated/EngineProcessModel.Generated.kt | 46 ++++++------------- .../generator/UtTestsDialogProcessor.kt | 11 ++--- .../intellij/plugin/process/EngineProcess.kt | 5 -- .../org/utbot/rd/models/EngineProcessModel.kt | 4 -- 12 files changed, 66 insertions(+), 109 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 9a7fc3406b..282f580e99 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 @@ -1149,14 +1149,12 @@ open class TypeParameters(val parameters: List = emptyList()) class WildcardTypeParameter : TypeParameters(emptyList()) /** - * Additional data describing user project. + * A context to use when no specific data is required. */ -interface ApplicationContext - -/** - * A context to use when no additional data is required. - */ -object EmptyApplicationContext: ApplicationContext +open class ApplicationContext( + val mockFrameworkInstalled: Boolean = true, + val staticsMockingIsConfigured: Boolean = true, +) /** * Data we get from Spring application context @@ -1164,9 +1162,11 @@ object EmptyApplicationContext: ApplicationContext * * @param beanQualifiedNames describes fqn of injected classes */ -data class SpringApplicationContext( +class SpringApplicationContext( + mockInstalled: Boolean, + staticsMockingIsConfigured: Boolean, val beanQualifiedNames: List = emptyList(), -): ApplicationContext { +): ApplicationContext(mockInstalled, staticsMockingIsConfigured) { private val springInjectedClasses: List by lazy { beanQualifiedNames.map { fqn -> utContext.classLoader.loadClass(fqn).id } } 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 ce6e37d16d..9e4698692e 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Mocks.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Mocks.kt @@ -19,6 +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.plugin.api.util.isInaccessibleViaReflection import soot.BooleanType import soot.RefType @@ -156,13 +157,30 @@ class Mocker( private val hierarchy: Hierarchy, chosenClassesToMockAlways: Set, internal val mockListenerController: MockListenerController? = null, + private val applicationContext: ApplicationContext, ) { - private val mocksDesired: Boolean = strategy != MockStrategy.NO_MOCKS + private val mocksAreDesired: Boolean = strategy != MockStrategy.NO_MOCKS + + fun construct(value: ObjectValue?, mockInfo: UtMockInfo): MockedObjectInfo { + return value + ?.let { + val mockingIsPossible = when (mockInfo) { + is UtFieldMockInfo, + is UtObjectMockInfo -> applicationContext.mockFrameworkInstalled + is UtNewInstanceMockInfo, + is UtStaticMethodMockInfo, + is UtStaticObjectMockInfo -> applicationContext.staticsMockingIsConfigured + } + val mockingIsForcedAndPossible = mockAlways(it.type) && mockingIsPossible - fun construct(value: ObjectValue?): MockedObjectInfo = - value - ?.let { if (mocksDesired || mockAlways(it.type)) ExpectedMock(it) else UnexpectedMock(it) } + if (mocksAreDesired || mockingIsForcedAndPossible) { + ExpectedMock(it) + } else { + UnexpectedMock(it) + } + } ?: NoMock + } /** * Creates mocked instance of the [type] using mock info if it should be mocked by the mocker, @@ -172,7 +190,7 @@ class Mocker( */ fun mock(type: RefType, mockInfo: UtMockInfo): MockedObjectInfo { val objectValue = if (shouldMock(type, mockInfo)) createMockObject(type, mockInfo) else null - return construct(objectValue) + return construct(objectValue, mockInfo) } /** @@ -183,7 +201,7 @@ class Mocker( mockListenerController?.onShouldMock(strategy, mockInfo) val objectValue = createMockObject(type, mockInfo) - return construct(objectValue) + return construct(objectValue, mockInfo) } /** 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 df538b4202..5912fa859b 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt @@ -1385,10 +1385,9 @@ class Traverser( val mockedObject = mockedObjectInfo.value if (mockedObjectInfo is UnexpectedMock) { - error("Wrong mocker configuration, it decided to mock $type object, although it is unexpected") + queuedSymbolicStateUpdates += nullEqualityConstraint.asHardConstraint() } - if (mockedObject != null) { queuedSymbolicStateUpdates += MemoryUpdate(mockInfos = persistentListOf(MockInfoEnriched(mockInfo))) 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 083efdde51..ac467db81c 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, - applicationContext: ApplicationContext?, + applicationContext: ApplicationContext, private val solverTimeoutInMillis: Int = checkSolverTimeoutMillis ) : UtContextInitializer() { private val graph = methodUnderTest.sootMethod.jimpleBody().apply { @@ -129,7 +129,8 @@ class UtBotSymbolicEngine( classUnderTest, hierarchy, chosenClassesToMockAlways, - MockListenerController(controller) + MockListenerController(controller), + applicationContext = applicationContext, ) fun attachMockListener(mockListener: MockListener) = mocker.mockListenerController?.attach(mockListener) diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/util/mockListeners/ForceMockListener.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/util/mockListeners/ForceMockListener.kt index 7ece9a40d7..ac9001dd39 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/util/mockListeners/ForceMockListener.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/util/mockListeners/ForceMockListener.kt @@ -8,25 +8,17 @@ import org.utbot.framework.util.ConflictTriggers /** * Listener for mocker events in [org.utbot.engine.UtBotSymbolicEngine]. - * If forced mock happened, cancels the engine job. * * Supposed to be created only if Mockito is not installed. */ -class ForceMockListener private constructor(triggers: ConflictTriggers, private val shouldCancelJob: Boolean): MockListener(triggers) { +class ForceMockListener private constructor(triggers: ConflictTriggers): MockListener(triggers) { override fun onShouldMock(controller: EngineController, strategy: MockStrategy, mockInfo: UtMockInfo) { - // If force mocking happened -- сancel engine job - if (shouldCancelJob) controller.job?.cancel(ForceMockCancellationException()) - triggers[Conflict.ForceMockHappened] = true } companion object { - fun create( - testCaseGenerator: TestCaseGenerator, - conflictTriggers: ConflictTriggers, - shouldCancelJob: Boolean = false, - ) : ForceMockListener { - val listener = ForceMockListener(conflictTriggers, shouldCancelJob) + fun create(testCaseGenerator: TestCaseGenerator, conflictTriggers: ConflictTriggers) : ForceMockListener { + val listener = ForceMockListener(conflictTriggers) testCaseGenerator.engineActions.add { engine -> engine.attachMockListener(listener) } return listener diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/util/mockListeners/ForceStaticMockListener.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/util/mockListeners/ForceStaticMockListener.kt index ba45a07c86..4bd3330c46 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/util/mockListeners/ForceStaticMockListener.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/util/mockListeners/ForceStaticMockListener.kt @@ -12,29 +12,17 @@ import org.utbot.framework.util.ConflictTriggers /** * Listener for mocker events in [org.utbot.engine.UtBotSymbolicEngine]. - * If forced static mock happened, cancels the engine job. * * Supposed to be created only if Mockito inline is not installed. */ -class ForceStaticMockListener private constructor(triggers: ConflictTriggers, private val shouldCancelJob: Boolean): MockListener(triggers) { +class ForceStaticMockListener private constructor(triggers: ConflictTriggers): MockListener(triggers) { override fun onShouldMock(controller: EngineController, strategy: MockStrategy, mockInfo: UtMockInfo) { - if (mockInfo is UtNewInstanceMockInfo - || mockInfo is UtStaticMethodMockInfo - || mockInfo is UtStaticObjectMockInfo) { - // If force static mocking happened -- сancel engine job - if (shouldCancelJob) controller.job?.cancel(ForceStaticMockCancellationException()) - triggers[Conflict.ForceStaticMockHappened] = true } - } companion object { - fun create( - testCaseGenerator: TestCaseGenerator, - conflictTriggers: ConflictTriggers, - shouldCancelJob: Boolean = false, - ) : ForceStaticMockListener { - val listener = ForceStaticMockListener(conflictTriggers, shouldCancelJob) + fun create(testCaseGenerator: TestCaseGenerator, conflictTriggers: ConflictTriggers) : ForceStaticMockListener { + val listener = ForceStaticMockListener(conflictTriggers) testCaseGenerator.engineActions.add { engine -> engine.attachMockListener(listener) } return listener 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 0a0eb8cae6..a7346a26c0 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 applicationContext: ApplicationContext? = null, + val applicationContext: ApplicationContext = ApplicationContext(), ) { 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, - applicationContext = null, + applicationContext, executionTimeEstimator, ) engineActions.map { engine.apply(it) } @@ -257,7 +257,7 @@ open class TestCaseGenerator( method: ExecutableId, mockStrategyApi: MockStrategyApi, chosenClassesToMockAlways: Set, - applicationContext: ApplicationContext?, + applicationContext: ApplicationContext, executionTimeEstimator: ExecutionTimeEstimator ): UtBotSymbolicEngine { logger.debug("Starting symbolic execution for $method --$mockStrategyApi--") 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 f00abfdd34..8a0d38720d 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 @@ -7,8 +7,6 @@ import kotlinx.coroutines.runBlocking import mu.KotlinLogging import org.utbot.analytics.AnalyticsConfigureUtil import org.utbot.common.* -import org.utbot.engine.util.mockListeners.ForceMockListener -import org.utbot.engine.util.mockListeners.ForceStaticMockListener import org.utbot.framework.codegen.* import org.utbot.framework.codegen.domain.HangingTestsTimeout import org.utbot.framework.codegen.domain.MockitoStaticMocking @@ -26,7 +24,6 @@ import org.utbot.framework.plugin.api.util.jClass import org.utbot.framework.plugin.api.util.method import org.utbot.framework.plugin.services.JdkInfo import org.utbot.framework.process.generated.* -import org.utbot.framework.util.ConflictTriggers import org.utbot.instrumentation.instrumentation.instrumenter.Instrumenter import org.utbot.instrumentation.util.KryoHelper import org.utbot.rd.IdleWatchdog @@ -99,19 +96,9 @@ private fun EngineProcessModel.setup(kryoHelper: KryoHelper, watchdog: IdleWatch watchdog.measureTimeForActiveCall(generate, "Generating tests") { params -> val methods: List = kryoHelper.readObject(params.methods) logger.debug().measureTime({ "starting generation for ${methods.size} methods, starting with ${methods.first()}" }) { - val mockFrameworkInstalled = params.mockInstalled - val conflictTriggers = ConflictTriggers(kryoHelper.readObject(params.conflictTriggers)) - if (!mockFrameworkInstalled) { - ForceMockListener.create(testGenerator, conflictTriggers, shouldCancelJob = true) - } - val staticsMockingConfigured = params.staticsMockingIsConfigureda - if (!staticsMockingConfigured) { - ForceStaticMockListener.create(testGenerator, conflictTriggers, shouldCancelJob = true) - } - val generateFlow = when (testGenerator.applicationContext) { is SpringApplicationContext -> defaultSpringFlow(params.generationTimeout) - is EmptyApplicationContext -> testFlow { + is ApplicationContext -> 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 9a136e45bd..420bb476f8 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 = -4839464828913070560L + const val serializationHash = -120710112541549600L } override val serializersOwner: ISerializersOwner get() = EngineProcessModel @@ -173,7 +173,7 @@ val IProtocol.engineProcessModel get() = getOrCreateExtension(EngineProcessModel /** - * #### Generated from [EngineProcessModel.kt:102] + * #### Generated from [EngineProcessModel.kt:98] */ data class FindMethodParamNamesArguments ( val classId: ByteArray, @@ -236,7 +236,7 @@ data class FindMethodParamNamesArguments ( /** - * #### Generated from [EngineProcessModel.kt:106] + * #### Generated from [EngineProcessModel.kt:102] */ data class FindMethodParamNamesResult ( val paramNames: ByteArray @@ -293,7 +293,7 @@ data class FindMethodParamNamesResult ( /** - * #### Generated from [EngineProcessModel.kt:95] + * #### Generated from [EngineProcessModel.kt:91] */ data class FindMethodsInClassMatchingSelectedArguments ( val classId: ByteArray, @@ -356,7 +356,7 @@ data class FindMethodsInClassMatchingSelectedArguments ( /** - * #### Generated from [EngineProcessModel.kt:99] + * #### Generated from [EngineProcessModel.kt:95] */ data class FindMethodsInClassMatchingSelectedResult ( val executableIds: ByteArray @@ -416,9 +416,6 @@ data class FindMethodsInClassMatchingSelectedResult ( * #### Generated from [EngineProcessModel.kt:44] */ data class GenerateParams ( - val mockInstalled: Boolean, - val staticsMockingIsConfigureda: Boolean, - val conflictTriggers: ByteArray, val methods: ByteArray, val mockStrategy: String, val chosenClassesToMockAlways: ByteArray, @@ -436,9 +433,6 @@ data class GenerateParams ( @Suppress("UNCHECKED_CAST") override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): GenerateParams { - val mockInstalled = buffer.readBool() - val staticsMockingIsConfigureda = buffer.readBool() - val conflictTriggers = buffer.readByteArray() val methods = buffer.readByteArray() val mockStrategy = buffer.readString() val chosenClassesToMockAlways = buffer.readByteArray() @@ -448,13 +442,10 @@ data class GenerateParams ( val isFuzzingEnabled = buffer.readBool() val fuzzingValue = buffer.readDouble() val searchDirectory = buffer.readString() - return GenerateParams(mockInstalled, staticsMockingIsConfigureda, conflictTriggers, methods, mockStrategy, chosenClassesToMockAlways, timeout, generationTimeout, isSymbolicEngineEnabled, isFuzzingEnabled, fuzzingValue, searchDirectory) + return GenerateParams(methods, mockStrategy, chosenClassesToMockAlways, timeout, generationTimeout, isSymbolicEngineEnabled, isFuzzingEnabled, fuzzingValue, searchDirectory) } override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: GenerateParams) { - buffer.writeBool(value.mockInstalled) - buffer.writeBool(value.staticsMockingIsConfigureda) - buffer.writeByteArray(value.conflictTriggers) buffer.writeByteArray(value.methods) buffer.writeString(value.mockStrategy) buffer.writeByteArray(value.chosenClassesToMockAlways) @@ -479,9 +470,6 @@ data class GenerateParams ( other as GenerateParams - if (mockInstalled != other.mockInstalled) return false - if (staticsMockingIsConfigureda != other.staticsMockingIsConfigureda) return false - if (!(conflictTriggers contentEquals other.conflictTriggers)) return false if (!(methods contentEquals other.methods)) return false if (mockStrategy != other.mockStrategy) return false if (!(chosenClassesToMockAlways contentEquals other.chosenClassesToMockAlways)) return false @@ -497,9 +485,6 @@ data class GenerateParams ( //hash code trait override fun hashCode(): Int { var __r = 0 - __r = __r*31 + mockInstalled.hashCode() - __r = __r*31 + staticsMockingIsConfigureda.hashCode() - __r = __r*31 + conflictTriggers.contentHashCode() __r = __r*31 + methods.contentHashCode() __r = __r*31 + mockStrategy.hashCode() __r = __r*31 + chosenClassesToMockAlways.contentHashCode() @@ -515,9 +500,6 @@ data class GenerateParams ( override fun print(printer: PrettyPrinter) { printer.println("GenerateParams (") printer.indent { - print("mockInstalled = "); mockInstalled.print(printer); println() - print("staticsMockingIsConfigureda = "); staticsMockingIsConfigureda.print(printer); println() - print("conflictTriggers = "); conflictTriggers.print(printer); println() print("methods = "); methods.print(printer); println() print("mockStrategy = "); mockStrategy.print(printer); println() print("chosenClassesToMockAlways = "); chosenClassesToMockAlways.print(printer); println() @@ -536,7 +518,7 @@ data class GenerateParams ( /** - * #### Generated from [EngineProcessModel.kt:62] + * #### Generated from [EngineProcessModel.kt:58] */ data class GenerateResult ( val notEmptyCases: Int, @@ -599,7 +581,7 @@ data class GenerateResult ( /** - * #### Generated from [EngineProcessModel.kt:114] + * #### Generated from [EngineProcessModel.kt:110] */ data class GenerateTestReportArgs ( val eventLogMessage: String?, @@ -692,7 +674,7 @@ data class GenerateTestReportArgs ( /** - * #### Generated from [EngineProcessModel.kt:123] + * #### Generated from [EngineProcessModel.kt:119] */ data class GenerateTestReportResult ( val notifyMessage: String, @@ -824,7 +806,7 @@ data class JdkInfo ( /** - * #### Generated from [EngineProcessModel.kt:90] + * #### Generated from [EngineProcessModel.kt:86] */ data class MethodDescription ( val name: String, @@ -893,7 +875,7 @@ data class MethodDescription ( /** - * #### Generated from [EngineProcessModel.kt:66] + * #### Generated from [EngineProcessModel.kt:62] */ data class RenderParams ( val testSetsId: Long, @@ -1034,7 +1016,7 @@ data class RenderParams ( /** - * #### Generated from [EngineProcessModel.kt:83] + * #### Generated from [EngineProcessModel.kt:79] */ data class RenderResult ( val generatedCode: String, @@ -1097,7 +1079,7 @@ data class RenderResult ( /** - * #### Generated from [EngineProcessModel.kt:87] + * #### Generated from [EngineProcessModel.kt:83] */ data class SetupContextParams ( val classpathForUrlsClassloader: List @@ -1235,7 +1217,7 @@ data class TestGeneratorParams ( /** - * #### Generated from [EngineProcessModel.kt:109] + * #### Generated from [EngineProcessModel.kt:105] */ 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 c43e0082b5..155273b6ab 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 @@ -26,7 +26,7 @@ import org.utbot.framework.CancellationStrategyType.NONE 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.EmptyApplicationContext +import org.utbot.framework.plugin.api.ApplicationContext import org.utbot.framework.plugin.api.JavaDocCommentStyle import org.utbot.framework.plugin.api.util.LockFile import org.utbot.framework.plugin.api.util.withStaticsSubstitutionRequired @@ -170,7 +170,10 @@ object UtTestsDialogProcessor { }.toMap() } - val applicationContext = EmptyApplicationContext + val applicationContext = ApplicationContext( + model.mockFramework.isInstalled, + model.staticsMocking.isConfigured, + ) // TODO: obtain bean definitions and other info from `utbot-spring-analyzer` //SpringApplicationContext(beanQualifiedNames = emptyList()) @@ -249,8 +252,6 @@ object UtTestsDialogProcessor { .executeSynchronously() withStaticsSubstitutionRequired(true) { - val mockFrameworkInstalled = model.mockFramework.isInstalled - val startTime = System.currentTimeMillis() val timerHandler = AppExecutorUtil.getAppScheduledExecutorService().scheduleWithFixedDelay({ @@ -266,8 +267,6 @@ object UtTestsDialogProcessor { }, 0, 500, TimeUnit.MILLISECONDS) try { val rdGenerateResult = process.generate( - mockFrameworkInstalled, - model.staticsMocking.isConfigured, model.conflictTriggers, methods, model.mockStrategy, 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 b0eec3639b..ef6b09fcc1 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 @@ -252,8 +252,6 @@ class EngineProcess private constructor(val project: Project, private val classN } fun generate( - mockInstalled: Boolean, - staticsMockingIsConfigured: Boolean, conflictTriggers: ConflictTriggers, methods: List, mockStrategyApi: MockStrategyApi, @@ -267,9 +265,6 @@ class EngineProcess private constructor(val project: Project, private val classN ): RdTestGenerationResult { assertReadAccessNotAllowed() val params = GenerateParams( - mockInstalled, - staticsMockingIsConfigured, - kryoHelper.writeObject(conflictTriggers.toMutableMap()), kryoHelper.writeObject(methods), mockStrategyApi.name, kryoHelper.writeObject(chosenClassesToMockAlways), 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 08484c8273..be656c25d1 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 @@ -42,10 +42,6 @@ object EngineProcessModel : Ext(EngineProcessRoot) { field("applicationContext", array(PredefinedType.byte)) } val generateParams = structdef { - // mocks - field("mockInstalled", PredefinedType.bool) - field("staticsMockingIsConfigureda", PredefinedType.bool) - field("conflictTriggers", array(PredefinedType.byte)) // generate field("methods", array(PredefinedType.byte)) field("mockStrategy", PredefinedType.string) From ee4823e9f0b4f7d950eba5ac544f005cad00f57f Mon Sep 17 00:00:00 2001 From: Egor Kulikov Date: Mon, 6 Mar 2023 17:02:13 +0300 Subject: [PATCH 09/12] Add some documentation --- .../kotlin/org/utbot/framework/plugin/api/Api.kt | 4 ++-- .../src/main/kotlin/org/utbot/engine/Mocks.kt | 16 ++++++++++------ .../main/kotlin/org/utbot/engine/Traverser.kt | 4 ++-- .../org/utbot/engine/UtBotSymbolicEngine.kt | 2 +- .../framework/plugin/api/TestCaseGenerator.kt | 4 ++-- .../utbot/framework/process/EngineProcessMain.kt | 4 ++-- .../plugin/generator/UtTestsDialogProcessor.kt | 4 ++-- 7 files changed, 21 insertions(+), 17 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 282f580e99..7ded4693f2 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 @@ -1151,7 +1151,7 @@ class WildcardTypeParameter : TypeParameters(emptyList()) /** * A context to use when no specific data is required. */ -open class ApplicationContext( +open class StandardApplicationContext( val mockFrameworkInstalled: Boolean = true, val staticsMockingIsConfigured: Boolean = true, ) @@ -1166,7 +1166,7 @@ class SpringApplicationContext( mockInstalled: Boolean, staticsMockingIsConfigured: Boolean, val beanQualifiedNames: List = emptyList(), -): ApplicationContext(mockInstalled, staticsMockingIsConfigured) { +): StandardApplicationContext(mockInstalled, staticsMockingIsConfigured) { private val springInjectedClasses: List by lazy { beanQualifiedNames.map { fqn -> utContext.classLoader.loadClass(fqn).id } } 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 9e4698692e..67eb596def 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.plugin.api.StandardApplicationContext import org.utbot.framework.plugin.api.util.isInaccessibleViaReflection import soot.BooleanType import soot.RefType @@ -157,10 +157,14 @@ class Mocker( private val hierarchy: Hierarchy, chosenClassesToMockAlways: Set, internal val mockListenerController: MockListenerController? = null, - private val applicationContext: ApplicationContext, + private val applicationContext: StandardApplicationContext, ) { private val mocksAreDesired: Boolean = strategy != MockStrategy.NO_MOCKS + /** + * Constructs [MockedObjectInfo]: enriches given value with + * an information if this mock is expected or not. + */ fun construct(value: ObjectValue?, mockInfo: UtMockInfo): MockedObjectInfo { return value ?.let { @@ -183,8 +187,8 @@ class Mocker( } /** - * Creates mocked instance of the [type] using mock info if it should be mocked by the mocker, - * otherwise returns null. + * Creates mocked instance (if it should be mocked by the mocker) of the [type] using [mockInfo] + * otherwise returns [NoMock]. * * @see shouldMock */ @@ -194,8 +198,8 @@ class Mocker( } /** - * Creates mocked instance of the [type] using mock info. Unlike to [mock], it does not - * check anything and always returns the constructed mock. + * Creates mocked instance of the [type] using [mockInfo] + * it does not check anything and always returns the constructed mock. */ fun forceMock(type: RefType, mockInfo: UtMockInfo): MockedObjectInfo { mockListenerController?.onShouldMock(strategy, mockInfo) 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 5912fa859b..d1c8a2568c 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt @@ -116,7 +116,7 @@ import org.utbot.framework.UtSettings import org.utbot.framework.UtSettings.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.StandardApplicationContext import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.ExecutableId import org.utbot.framework.plugin.api.FieldId @@ -240,7 +240,7 @@ class Traverser( internal val typeResolver: TypeResolver, private val globalGraph: InterProceduralUnitGraph, private val mocker: Mocker, - private val applicationContext: ApplicationContext?, + private val applicationContext: StandardApplicationContext?, ) : UtContextInitializer() { private val visitedStmts: MutableSet = mutableSetOf() 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 ac467db81c..2b913f31eb 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, - applicationContext: ApplicationContext, + applicationContext: StandardApplicationContext, private val solverTimeoutInMillis: Int = checkSolverTimeoutMillis ) : UtContextInitializer() { private val graph = methodUnderTest.sootMethod.jimpleBody().apply { 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 a7346a26c0..1fadbc6698 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 applicationContext: ApplicationContext = ApplicationContext(), + val applicationContext: StandardApplicationContext = StandardApplicationContext(), ) { private val logger: KLogger = KotlinLogging.logger {} private val timeoutLogger: KLogger = KotlinLogging.logger(logger.name + ".timeout") @@ -257,7 +257,7 @@ open class TestCaseGenerator( method: ExecutableId, mockStrategyApi: MockStrategyApi, chosenClassesToMockAlways: Set, - applicationContext: ApplicationContext, + applicationContext: StandardApplicationContext, executionTimeEstimator: ExecutionTimeEstimator ): UtBotSymbolicEngine { logger.debug("Starting symbolic execution for $method --$mockStrategyApi--") 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 8a0d38720d..84f3cda3c4 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 @@ -80,7 +80,7 @@ private fun EngineProcessModel.setup(kryoHelper: KryoHelper, watchdog: IdleWatch watchdog.measureTimeForActiveCall(createTestGenerator, "Creating Test Generator") { params -> AnalyticsConfigureUtil.configureML() Instrumenter.adapter = RdInstrumenter(realProtocol.rdInstrumenterAdapter) - val applicationContext: ApplicationContext = kryoHelper.readObject(params.applicationContext) + val applicationContext: StandardApplicationContext = kryoHelper.readObject(params.applicationContext) testGenerator = TestCaseGenerator(buildDirs = params.buildDir.map { Paths.get(it) }, classpath = params.classpath, @@ -98,7 +98,7 @@ private fun EngineProcessModel.setup(kryoHelper: KryoHelper, watchdog: IdleWatch logger.debug().measureTime({ "starting generation for ${methods.size} methods, starting with ${methods.first()}" }) { val generateFlow = when (testGenerator.applicationContext) { is SpringApplicationContext -> defaultSpringFlow(params.generationTimeout) - is ApplicationContext -> testFlow { + is StandardApplicationContext -> testFlow { generationTimeout = params.generationTimeout isSymbolicEngineEnabled = params.isSymbolicEngineEnabled isFuzzingEnabled = params.isFuzzingEnabled 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 155273b6ab..6bda5c9f2d 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 @@ -26,7 +26,7 @@ import org.utbot.framework.CancellationStrategyType.NONE 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.ApplicationContext +import org.utbot.framework.plugin.api.StandardApplicationContext import org.utbot.framework.plugin.api.JavaDocCommentStyle import org.utbot.framework.plugin.api.util.LockFile import org.utbot.framework.plugin.api.util.withStaticsSubstitutionRequired @@ -170,7 +170,7 @@ object UtTestsDialogProcessor { }.toMap() } - val applicationContext = ApplicationContext( + val applicationContext = StandardApplicationContext( model.mockFramework.isInstalled, model.staticsMocking.isConfigured, ) From aae1641001aee99b876c01f1018c09efe656a5e6 Mon Sep 17 00:00:00 2001 From: Egor Kulikov Date: Mon, 6 Mar 2023 17:09:36 +0300 Subject: [PATCH 10/12] Add some documentation --- .../src/main/kotlin/org/utbot/framework/plugin/api/Api.kt | 3 +++ .../src/main/kotlin/org/utbot/engine/Traverser.kt | 1 - .../org/utbot/intellij/plugin/process/EngineProcess.kt | 7 +------ 3 files changed, 4 insertions(+), 7 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 7ded4693f2..a41521e36f 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 @@ -1150,6 +1150,9 @@ class WildcardTypeParameter : TypeParameters(emptyList()) /** * 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 StandardApplicationContext( val mockFrameworkInstalled: Boolean = true, 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 d1c8a2568c..232f475a2b 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt @@ -121,7 +121,6 @@ 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.SpringApplicationContext import org.utbot.framework.plugin.api.classId import org.utbot.framework.plugin.api.id import org.utbot.framework.plugin.api.util.executable 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 ef6b09fcc1..13f735d3ea 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 @@ -10,10 +10,6 @@ import com.intellij.psi.impl.file.impl.JavaFileManager import com.intellij.psi.search.GlobalSearchScope import com.intellij.refactoring.util.classMembers.MemberInfo import com.jetbrains.rd.util.ConcurrentHashMap -import com.jetbrains.rd.util.ILoggerFactory -import com.jetbrains.rd.util.Logger -import com.jetbrains.rd.util.Statics -import com.jetbrains.rd.util.lifetime.Lifetime import com.jetbrains.rd.util.lifetime.LifetimeDefinition import kotlinx.coroutines.runBlocking import mu.KotlinLogging @@ -42,7 +38,6 @@ import org.utbot.rd.generated.SettingForResult import org.utbot.rd.generated.SettingsModel import org.utbot.rd.generated.settingsModel import org.utbot.rd.generated.synchronizationModel -import org.utbot.rd.loggers.UtRdKLoggerFactory import org.utbot.rd.loggers.overrideDefaultRdLoggerFactoryWithKLogger import org.utbot.sarif.SourceFindingStrategy import java.io.File @@ -188,7 +183,7 @@ class EngineProcess private constructor(val project: Project, private val classN classPath: String?, dependencyPaths: String, jdkInfo: JdkInfo, - applicationContext: ApplicationContext, + applicationContext: StandardApplicationContext, isCancelled: (Unit) -> Boolean ) { assertReadAccessNotAllowed() From fb0d0813de57aa221cbb05a8442feb62c57968a4 Mon Sep 17 00:00:00 2001 From: Egor Kulikov Date: Tue, 7 Mar 2023 11:01:31 +0300 Subject: [PATCH 11/12] Apply fixes under review --- .../org/utbot/framework/plugin/api/Api.kt | 8 ++- .../src/main/kotlin/org/utbot/engine/Mocks.kt | 53 +++++++++++-------- .../main/kotlin/org/utbot/engine/Traverser.kt | 22 +++++--- 3 files changed, 54 insertions(+), 29 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 a41521e36f..f3a53670cd 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 @@ -1157,7 +1157,13 @@ class WildcardTypeParameter : TypeParameters(emptyList()) open class StandardApplicationContext( val mockFrameworkInstalled: Boolean = true, val staticsMockingIsConfigured: Boolean = true, -) +) { + init { + if (!mockFrameworkInstalled) { + require(!staticsMockingIsConfigured) { "Static mocking cannot be used without mock framework" } + } + } +} /** * Data we get from Spring application context 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 67eb596def..bb5b26e26f 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Mocks.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Mocks.kt @@ -132,21 +132,30 @@ data class UtStaticMethodMockInfo( /** * A wrapper for [ObjectValue] to store additional onfo. */ -sealed class MockedObjectInfo(val value: ObjectValue?) +sealed class MockedObjectInfo { + abstract val value: ObjectValue? +} -object NoMock: MockedObjectInfo(value = null) +object NoMock: MockedObjectInfo() { + override val value: ObjectValue? = null +} /** * Represents a mock that occurs when mock strategy allows it * or when an object type is in a set that requires force mocking. */ -class ExpectedMock(value: ObjectValue): MockedObjectInfo(value) +class ExpectedMock(objectValue: ObjectValue): MockedObjectInfo() { + override val value: ObjectValue = objectValue +} /** * Represents a mock that occurs when it is not recommended - * E.g. mock strategy recommends do not mock + * E.g. mock framework is not installed or + * mock startegy is DO_NOT_MOCK and class is not in mockAlways set. */ -class UnexpectedMock(value: ObjectValue): MockedObjectInfo(value) +class UnexpectedMock(objectValue: ObjectValue): MockedObjectInfo() { + override val value: ObjectValue = objectValue +} /** * Service to mock things. Knows mock strategy, class under test and class hierarchy. @@ -166,24 +175,24 @@ class Mocker( * an information if this mock is expected or not. */ fun construct(value: ObjectValue?, mockInfo: UtMockInfo): MockedObjectInfo { - return value - ?.let { - val mockingIsPossible = when (mockInfo) { - is UtFieldMockInfo, - is UtObjectMockInfo -> applicationContext.mockFrameworkInstalled - is UtNewInstanceMockInfo, - is UtStaticMethodMockInfo, - is UtStaticObjectMockInfo -> applicationContext.staticsMockingIsConfigured - } - val mockingIsForcedAndPossible = mockAlways(it.type) && mockingIsPossible + if (value == null) { + return NoMock + } - if (mocksAreDesired || mockingIsForcedAndPossible) { - ExpectedMock(it) - } else { - UnexpectedMock(it) - } - } - ?: NoMock + val mockingIsPossible = when (mockInfo) { + is UtFieldMockInfo, + is UtObjectMockInfo -> applicationContext.mockFrameworkInstalled + is UtNewInstanceMockInfo, + is UtStaticMethodMockInfo, + is UtStaticObjectMockInfo -> applicationContext.staticsMockingIsConfigured + } + val mockingIsForcedAndPossible = mockAlways(value.type) && mockingIsPossible + + return if (mocksAreDesired || mockingIsForcedAndPossible) { + ExpectedMock(value) + } else { + UnexpectedMock(value) + } } /** 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 232f475a2b..21e3f430b4 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt @@ -1384,6 +1384,9 @@ class Traverser( val mockedObject = mockedObjectInfo.value if (mockedObjectInfo is UnexpectedMock) { + // if mock occurs, but it is unexpected due to some reasons + // (e.g. we do not have mock framework installed), + // we can only generate a test that uses null value for mocked object queuedSymbolicStateUpdates += nullEqualityConstraint.asHardConstraint() } @@ -1474,10 +1477,17 @@ class Traverser( val mockInfo = mockInfoGenerator.generate(addr) val mockedObjectInfo = mocker.forceMock(type, mockInfoGenerator.generate(addr)) - val mockedObject = mockedObjectInfo.value ?: error("Mocked value cannot be null after force mock") - if (mockedObjectInfo is UnexpectedMock) { - queuedSymbolicStateUpdates += nullEqualityConstraint.asHardConstraint() - return mockedObject + val mockedObject: ObjectValue = when (mockedObjectInfo) { + is NoMock -> error("Value must be mocked after the fore mock") + is ExpectedMock -> mockedObjectInfo.value + is UnexpectedMock -> { + // if mock occurs, but it is unexpected due to some reasons + // (e.g. we do not have mock framework installed), + // we can only generate a test that uses null value for mocked object + queuedSymbolicStateUpdates += nullEqualityConstraint.asHardConstraint() + + mockedObjectInfo.value + } } queuedSymbolicStateUpdates += MemoryUpdate(mockInfos = persistentListOf(MockInfoEnriched(mockInfo))) @@ -2680,7 +2690,7 @@ class Traverser( } ?: findMethodInvocationTargets(types, methodSubSignature) return methodInvocationTargets - .mapNotNull { (method, implementationClass, possibleTypes) -> + .map { (method, implementationClass, possibleTypes) -> val typeStorage = typeResolver.constructTypeStorage(implementationClass, possibleTypes) val mockInfo = memory.mockInfoByAddr(instance.addr) val mockedObjectInfo = mockInfo?.let { @@ -2710,7 +2720,7 @@ class Traverser( InvocationTarget(wrapperOrInstance, method, constraints) } is ExpectedMock -> { - val mockedObject = mockedObjectInfo.value!! + val mockedObject = mockedObjectInfo.value val typeConstraint = typeRegistry.typeConstraint(mockedObject.addr, mockedObject.typeStorage) val constraints = setOf(typeConstraint.isOrNullConstraint()) From efe2b6292ecf287d487fe839a3edafa22be2a042 Mon Sep 17 00:00:00 2001 From: Egor Kulikov Date: Thu, 9 Mar 2023 23:58:48 +0300 Subject: [PATCH 12/12] Fix review comments --- .../examples/mock/CommonMocksExampleTest.kt | 16 ++--- .../src/main/kotlin/org/utbot/engine/Mocks.kt | 62 +++++++++---------- .../main/kotlin/org/utbot/engine/Traverser.kt | 18 +++++- 3 files changed, 56 insertions(+), 40 deletions(-) diff --git a/utbot-framework-test/src/test/kotlin/org/utbot/examples/mock/CommonMocksExampleTest.kt b/utbot-framework-test/src/test/kotlin/org/utbot/examples/mock/CommonMocksExampleTest.kt index 3b80d7801a..ae2609d787 100644 --- a/utbot-framework-test/src/test/kotlin/org/utbot/examples/mock/CommonMocksExampleTest.kt +++ b/utbot-framework-test/src/test/kotlin/org/utbot/examples/mock/CommonMocksExampleTest.kt @@ -3,19 +3,21 @@ package org.utbot.examples.mock import org.utbot.framework.plugin.api.MockStrategyApi import org.junit.jupiter.api.Test import org.utbot.testcheckers.eq -import org.utbot.testing.DoNotCalculate import org.utbot.testing.UtValueTestCaseChecker +import org.utbot.testing.atLeast internal class CommonMocksExampleTest: UtValueTestCaseChecker(testClass = CommonMocksExample::class) { + //TODO: coverage values here require further investigation by experts + @Test fun testMockInterfaceWithoutImplementorsWithNoMocksStrategy() { checkMocks( CommonMocksExample::mockInterfaceWithoutImplementors, eq(1), { v, mocks, _ -> v == null && mocks.isEmpty() }, - coverage = DoNotCalculate, mockStrategy = MockStrategyApi.NO_MOCKS, + coverage = atLeast(75), ) } @@ -26,8 +28,8 @@ internal class CommonMocksExampleTest: UtValueTestCaseChecker(testClass = Common eq(2), { v, mocks, _ -> v == null && mocks.isEmpty() }, { _, mocks, _ -> mocks.singleOrNull() != null }, - coverage = DoNotCalculate, mockStrategy = MockStrategyApi.OTHER_CLASSES, + coverage = atLeast(75), ) } @@ -40,7 +42,7 @@ internal class CommonMocksExampleTest: UtValueTestCaseChecker(testClass = Common { fst, _, mocks, _ -> fst == null && mocks.isEmpty() }, { _, _, mocks, _ -> mocks.isEmpty() }, // should be changed to not null fst when 1449 will be finished mockStrategy = MockStrategyApi.OTHER_PACKAGES, - coverage = DoNotCalculate + coverage = atLeast(75) ) } @@ -55,7 +57,7 @@ internal class CommonMocksExampleTest: UtValueTestCaseChecker(testClass = Common // node == node.next // node.next.value == node.value + 1 mockStrategy = MockStrategyApi.OTHER_CLASSES, - coverage = DoNotCalculate + coverage = atLeast(13) ) } @@ -66,7 +68,7 @@ internal class CommonMocksExampleTest: UtValueTestCaseChecker(testClass = Common eq(1), { r -> r == -420 }, mockStrategy = MockStrategyApi.OTHER_CLASSES, - coverage = DoNotCalculate + coverage = atLeast(70), ) } @@ -76,7 +78,7 @@ internal class CommonMocksExampleTest: UtValueTestCaseChecker(testClass = Common CommonMocksExample::mocksForNullOfDifferentTypes, eq(1), mockStrategy = MockStrategyApi.OTHER_PACKAGES, - coverage = DoNotCalculate + coverage = atLeast(75) ) } } 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 bb5b26e26f..27b436e24d 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Mocks.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Mocks.kt @@ -130,7 +130,7 @@ data class UtStaticMethodMockInfo( ) : UtMockInfo(methodId.classId, addr) /** - * A wrapper for [ObjectValue] to store additional onfo. + * A wrapper for [ObjectValue] to store additional info. */ sealed class MockedObjectInfo { abstract val value: ObjectValue? @@ -142,16 +142,18 @@ object NoMock: MockedObjectInfo() { /** * Represents a mock that occurs when mock strategy allows it - * or when an object type is in a set that requires force mocking. + * or when an object type requires always requires mocking. + * + * See [Mocker.mockAlways] for more details. */ class ExpectedMock(objectValue: ObjectValue): MockedObjectInfo() { override val value: ObjectValue = objectValue } /** - * Represents a mock that occurs when it is not recommended + * Represents a mock that occurs when it is not allowed. * E.g. mock framework is not installed or - * mock startegy is DO_NOT_MOCK and class is not in mockAlways set. + * mock strategy is [MockStrategy.NO_MOCKS] and class is not in [Mocker.mockAlways] set. */ class UnexpectedMock(objectValue: ObjectValue): MockedObjectInfo() { override val value: ObjectValue = objectValue @@ -170,31 +172,6 @@ class Mocker( ) { private val mocksAreDesired: Boolean = strategy != MockStrategy.NO_MOCKS - /** - * Constructs [MockedObjectInfo]: enriches given value with - * an information if this mock is expected or not. - */ - fun construct(value: ObjectValue?, mockInfo: UtMockInfo): MockedObjectInfo { - if (value == null) { - return NoMock - } - - val mockingIsPossible = when (mockInfo) { - is UtFieldMockInfo, - is UtObjectMockInfo -> applicationContext.mockFrameworkInstalled - is UtNewInstanceMockInfo, - is UtStaticMethodMockInfo, - is UtStaticObjectMockInfo -> applicationContext.staticsMockingIsConfigured - } - val mockingIsForcedAndPossible = mockAlways(value.type) && mockingIsPossible - - return if (mocksAreDesired || mockingIsForcedAndPossible) { - ExpectedMock(value) - } else { - UnexpectedMock(value) - } - } - /** * Creates mocked instance (if it should be mocked by the mocker) of the [type] using [mockInfo] * otherwise returns [NoMock]. @@ -207,8 +184,7 @@ class Mocker( } /** - * Creates mocked instance of the [type] using [mockInfo] - * it does not check anything and always returns the constructed mock. + * Unlike to [mock], unconditionally creates a mocked instance of the [type] using [mockInfo]. */ fun forceMock(type: RefType, mockInfo: UtMockInfo): MockedObjectInfo { mockListenerController?.onShouldMock(strategy, mockInfo) @@ -241,6 +217,30 @@ class Mocker( } } + /** + * Constructs [MockedObjectInfo]: enriches given [mockedValue] with an information if mocking is expected or not. + */ + private fun construct(mockedValue: ObjectValue?, mockInfo: UtMockInfo): MockedObjectInfo { + if (mockedValue == null) { + return NoMock + } + + val mockingIsPossible = when (mockInfo) { + is UtFieldMockInfo, + is UtObjectMockInfo -> applicationContext.mockFrameworkInstalled + is UtNewInstanceMockInfo, + is UtStaticMethodMockInfo, + is UtStaticObjectMockInfo -> applicationContext.staticsMockingIsConfigured + } + val mockingIsForcedAndPossible = mockAlways(mockedValue.type) && mockingIsPossible + + return if (mocksAreDesired || mockingIsForcedAndPossible) { + ExpectedMock(mockedValue) + } else { + UnexpectedMock(mockedValue) + } + } + private fun checkIfShouldMock( type: RefType, mockInfo: UtMockInfo 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 21e3f430b4..cf4bbdae0c 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt @@ -1382,14 +1382,15 @@ class Traverser( val mockedObjectInfo = mocker.mock(type, mockInfo) - val mockedObject = mockedObjectInfo.value if (mockedObjectInfo is UnexpectedMock) { // if mock occurs, but it is unexpected due to some reasons // (e.g. we do not have mock framework installed), // we can only generate a test that uses null value for mocked object queuedSymbolicStateUpdates += nullEqualityConstraint.asHardConstraint() + return mockedObjectInfo.value } + val mockedObject = mockedObjectInfo.value if (mockedObject != null) { queuedSymbolicStateUpdates += MemoryUpdate(mockInfos = persistentListOf(MockInfoEnriched(mockInfo))) @@ -1478,7 +1479,7 @@ class Traverser( val mockedObjectInfo = mocker.forceMock(type, mockInfoGenerator.generate(addr)) val mockedObject: ObjectValue = when (mockedObjectInfo) { - is NoMock -> error("Value must be mocked after the fore mock") + is NoMock -> error("Value must be mocked after the force mock") is ExpectedMock -> mockedObjectInfo.value is UnexpectedMock -> { // if mock occurs, but it is unexpected due to some reasons @@ -1490,6 +1491,10 @@ class Traverser( } } + if (mockedObjectInfo is UnexpectedMock) { + return mockedObject + } + queuedSymbolicStateUpdates += MemoryUpdate(mockInfos = persistentListOf(MockInfoEnriched(mockInfo))) // add typeConstraint for mocked object. It's a declared type of the object. @@ -2728,6 +2733,15 @@ class Traverser( // TODO isMock???? InvocationTarget(mockedObject, method, constraints) } + /* + Currently, it is unclear how this could happen. + Perhaps, the answer is somewhere in the following situation: + you have an interface with an abstract method `foo`, and it has an abstract inheritor with the implementation of the method, + but this inheritor doesn't have any concrete inheritors. It looks like in this case we would mock this instance + (because it doesn't have any possible concrete type), but it is impossible since either this class cannot present + in possible types of the object on which we call `foo` (since they contain only concrete types), + or this class would be already mocked (since it doesn't contain any concrete implementors). + */ is UnexpectedMock -> unreachableBranch("If it ever happens, it should be investigated") } }