Skip to content

Conversation

@nikitavlaev
Copy link
Member

@nikitavlaev nikitavlaev commented Jun 22, 2022

Description

Added mock listeners for engine to make it abstract from external events.
Added force mock listener.
Added url handler for urls like "#utbot/..." to launch configuration tasks from popups.
Added message with link to mock framework configuration to test reports if force mocking happened.

Fixes #190

Type of Change

Please delete options that are not relevant.

  • Bug fix (non-breaking change which fixes an issue)

How Has This Been Tested?

Manual Scenario

Launched project without mockito installed.
Generated tests for Sort.quickSort without mocks -> mocks for Random were forced.
Checked that message suggesting mockito installation appeared in test report.
Verified that link in that message leads to the window for mockito configuration, and it installs correctly.
After installation, tests with force-mocks were generated correctly.

Checklist (remove irrelevant options):

  • The change followed the style guidelines of the UTBot project
  • Self-review of the code is passed
  • The change contains enough commentaries, particularly in hard-to-understand areas
  • New documentation is provided or existed one is altered
  • No new warnings
  • Tests that prove my change is effective
  • All tests pass locally with my changes
fun shouldMock(
type: RefType,
mockInfo: UtMockInfo,
): Boolean = doShouldMock(type, mockInfo).also { mockListenerController.onShouldMock(strategy, it) }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we emit an event if and only if doShouldMock returns true?

import kotlinx.coroutines.currentCoroutineContext
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Job
import kotlinx.coroutines.*
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct your Intellij Idea settings to avoid wildcard imports, please. In my situation it was reset after the update of IDE.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check it in other files of this PR too, please

)
}

fun attachMockListener(mockListener: MockListener) = mocker.mockListenerController.attach(mockListener)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's discuss why this code is a pat of engine, may be there is a very unclear relation here

import org.utbot.engine.util.mockListeners.exceptions.ForceMockCancellationException

/**
* Listener for mocker events in symbolic engine.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is always better to mention entities in comments as named references like [UtSymbolicEngine]

* Supposed to be created only if Mockito is not installed.
*/
class ForceMockListener: MockListener {
var forceMockHappened = false
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's avoid public setter on it

// If force mocking happened
if (shouldMock) {
// Cancel engine job
controller.job!!.cancel(ForceMockCancellationException())
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that we would never like to crash here. Suggest to do like this
controller.job?.let { ForceMockCancellationException() }

* Controller that allows to attach listeners to mocker in symbolic engine.
*/
class MockListenerController(private val controller: EngineController) {
val listeners = mutableListOf<MockListener>()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What other listeners except MockListener may be in MockListenerController?

if (model.forceMockingHappened) {
val warningMessage = """
Warning: Some test cases were ignored, because no mocking framework is installed in the project.
Better results could be achieved by <a href="${TestReportUrlOpeningListener.prefix}${TestReportUrlOpeningListener.mockitoSuffix}">installing mocking framework</a>, e.g. Mockito.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like "e. g. Mockito"
If user install another framework. it will not help him. More than that, mockito-core with the version startting from 3.. is required to him.

if (createMockFrameworkNotificationDialog("Configure mock framework") == Messages.YES) {
configureMockFramework(project!!, module!!)
}
} ?: error("Could not configure mock framework: null in module ")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need module or project name here: these message in logs will be just scaring

private val defaultListener = NotificationListener.UrlOpeningListener(false)

// Last project and module to be able to use them when activated for configuration tasks.
var project: Project? = null
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should they really be nullable?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

configureMockFramework(project!!, module!!) is very bad later

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, they should really be nullable. The other approach is to use lateinit var, but this will cause tight coupling with TestReportNotifier and force initialization of such properties at some point in the caller code, which is not obvious.


private fun handleDescription(descriptionSuffix: String) {
when {
descriptionSuffix.startsWith(mockitoSuffix) -> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May be we should store a list of all possible suffixes and do smth like when here?

}

forceMockListener?.run {
model.forceMockingHappened = forceMockHappened
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make the names similar, please

val notifyMessage = buildString {
appendHtmlLine(testsCodeWithTestReport.testsGenerationReport.toString())
appendHtmlLine()
if (model.forceMockingHappened) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually it is not very scalable solution, but I'm not sure it is easy to do better.

Comment on lines 130 to 131
}
else {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Odd formatting

descriptionSuffix.startsWith(mockitoSuffix) -> {
project?.let { module?.let {
if (createMockFrameworkNotificationDialog("Configure mock framework") == Messages.YES) {
configureMockFramework(project!!, module!!)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use it for the module and introduce a variable for the project. Otherwise, why would you use let instead of just if

mockInfo: UtMockInfo,
): Boolean = doShouldMock(type, mockInfo).also { mockListenerController.onShouldMock(strategy, it) }

private fun doShouldMock(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is a strange name. checkIfShouldMock or something like that might be more suitable

private val strategy: MockStrategy,
private val classUnderTest: ClassId,
private val hierarchy: Hierarchy,
internal val mockListenerController: MockListenerController,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure that it is a good idea for the Mocker to have a mandatory field with the controller. What if I don't know anything about it and just want to check whether some field would be mocked or not? What should I pass here? Or now Mocker cannot work without the engine (more specifically, without a current version of the engine, with the controller)

ForceMockListener().apply {
codeGenerator.generator.configureEngine = { engine -> engine.attachMockListener(this) }
}
} else null
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I've mentioned many times, brackets

@EgorkaKulikov
Copy link
Collaborator

Approve in accordance with our verbal discussion on improvements

Copy link
Member

@CaelmBleidd CaelmBleidd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is an odd-looking connection between the mocker, its controllers, engine controllers and listeners. Seems like it is a problem with the design and it might cause further problems with engine refactorings and separation of the virtual machine and traverse

Approved since the PR contains required fix and need to be tested ASAP

@nikitavlaev nikitavlaev force-pushed the nikitavlaev/force-mock-fix branch from 5bb59b3 to 1be046f Compare June 28, 2022 05:54
Added mock listeners for engine. Added force mock listener. Added url handler for urls like "#utbot/...". Added message with link to mock framework configuration to test reports if force mocking happened.
@nikitavlaev nikitavlaev force-pushed the nikitavlaev/force-mock-fix branch from 1be046f to 3d34399 Compare June 28, 2022 05:59
@nikitavlaev nikitavlaev enabled auto-merge (rebase) June 28, 2022 06:09
@nikitavlaev nikitavlaev merged commit 1ac3a04 into main Jun 28, 2022
@nikitavlaev nikitavlaev deleted the nikitavlaev/force-mock-fix branch June 28, 2022 07:21
@nikitavlaev nikitavlaev restored the nikitavlaev/force-mock-fix branch June 28, 2022 07:36
@denis-fokin denis-fokin added the priority-top-focus Top priority chosen by dev team label Jul 6, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

priority-top-focus Top priority chosen by dev team

4 participants