diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/CodeGenerationController.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/CodeGenerationController.kt index cc9f876f33..3b5761f053 100644 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/CodeGenerationController.kt +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator/CodeGenerationController.kt @@ -8,6 +8,7 @@ import com.intellij.ide.fileTemplates.FileTemplateUtil import com.intellij.ide.fileTemplates.JavaTemplateUtil import com.intellij.ide.highlighter.JavaFileType import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.application.readAction import com.intellij.openapi.application.runReadAction import com.intellij.openapi.application.runWriteAction import com.intellij.openapi.command.WriteCommandAction.runWriteCommandAction @@ -225,7 +226,7 @@ object CodeGenerationController { .doInspections(AnalysisScope(model.project)) } - private fun proceedTestReport(proc: EngineProcess, model: GenerateTestsModel) = runReadAction { + private fun proceedTestReport(proc: EngineProcess, model: GenerateTestsModel) { try { // Parametrized tests are not supported in tests report yet // TODO JIRA:1507 @@ -675,8 +676,7 @@ object CodeGenerationController { run(THREAD_POOL, indicator, "Rendering test code") { val (generatedTestsCode, utilClassKind) = try { val paramNames = try { - DumbService.getInstance(model.project) - .runReadActionInSmartMode(Computable { proc.findMethodParamNames(classUnderTest, classMethods) }) + proc.findMethodParamNames(classUnderTest, classMethods) } catch (e: Exception) { logger.warn(e) { "Cannot find method param names for ${classUnderTest.name}" } reportsCountDown.countDown() @@ -833,24 +833,26 @@ object CodeGenerationController { } - private fun eventLogMessage(project: Project): String? { - if (ToolWindowManager.getInstance(project).getToolWindow("Event Log") != null) - return """ + private fun eventLogMessage(project: Project): String? = runReadAction { + return@runReadAction if (ToolWindowManager.getInstance(project).getToolWindow("Event Log") != null) + """ See details in Event Log. """.trimIndent() - return null + else null } private fun showTestsReport(proc: EngineProcess, model: GenerateTestsModel) { val (notifyMessage, statistics, hasWarnings) = proc.generateTestsReport(model, eventLogMessage(model.project)) - if (hasWarnings) { - WarningTestsReportNotifier.notify(notifyMessage) - } else { - TestsReportNotifier.notify(notifyMessage) - } + runReadAction { + if (hasWarnings) { + WarningTestsReportNotifier.notify(notifyMessage) + } else { + TestsReportNotifier.notify(notifyMessage) + } - statistics?.let { DetailsTestsReportNotifier.notify(it) } + statistics?.let { DetailsTestsReportNotifier.notify(it) } + } } @Suppress("unused") 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 7f62ca55cf..cacfbe7f45 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 @@ -192,28 +192,30 @@ object UtTestsDialogProcessor { } val (methods, className) = process.executeWithTimeoutSuspended { + var canonicalName = "" + var srcMethods: List = emptyList() DumbService.getInstance(project) - .runReadActionInSmartMode(Computable { - val canonicalName = srcClass.canonicalName - val classId = process.obtainClassId(canonicalName) - psi2KClass[srcClass] = classId - val srcMethods = if (model.extractMembersFromSrcClasses) { - val chosenMethods = - model.selectedMembers.filter { it.member is PsiMethod } - val chosenNestedClasses = - model.selectedMembers.mapNotNull { it.member as? PsiClass } - chosenMethods + chosenNestedClasses.flatMap { - it.extractClassMethodsIncludingNested(false) + .runReadActionInSmartMode(Computable { + canonicalName = srcClass.canonicalName + srcMethods = if (model.extractMembersFromSrcClasses) { + val chosenMethods = + model.selectedMembers.filter { it.member is PsiMethod } + val chosenNestedClasses = + model.selectedMembers.mapNotNull { it.member as? PsiClass } + chosenMethods + chosenNestedClasses.flatMap { + it.extractClassMethodsIncludingNested(false) + } + } else { + srcClass.extractClassMethodsIncludingNested(false) } - } else { - srcClass.extractClassMethodsIncludingNested(false) - } - process.findMethodsInClassMatchingSelected( - classId, - srcMethods - ) to srcClass.name - }) - } + }) + val classId = process.obtainClassId(canonicalName) + psi2KClass[srcClass] = classId + process.findMethodsInClassMatchingSelected( + classId, + srcMethods + ) to srcClass.name + } if (methods.isEmpty()) { logger.error { "No methods matching selected found in class $className." } 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 f533110457..99a4117b04 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 @@ -1,8 +1,10 @@ package org.utbot.intellij.plugin.process import com.intellij.ide.plugins.cl.PluginClassLoader +import com.intellij.openapi.application.runReadAction import com.intellij.openapi.project.DumbService import com.intellij.openapi.project.Project +import com.intellij.openapi.util.Computable import com.intellij.psi.PsiMethod import com.intellij.psi.impl.file.impl.JavaFileManager import com.intellij.psi.search.GlobalSearchScope @@ -30,8 +32,7 @@ import org.utbot.instrumentation.util.KryoHelper import org.utbot.intellij.plugin.UtbotBundle import org.utbot.intellij.plugin.models.GenerateTestsModel import org.utbot.intellij.plugin.ui.TestReportUrlOpeningListener -import org.utbot.intellij.plugin.util.assertIsNonDispatchThread -import org.utbot.intellij.plugin.util.assertIsReadAccessAllowed +import org.utbot.intellij.plugin.util.assertReadAccessNotAllowed import org.utbot.intellij.plugin.util.methodDescription import org.utbot.rd.* import org.utbot.rd.exceptions.InstantProcessDeathException @@ -164,7 +165,8 @@ class EngineProcess private constructor(val project: Project, rdProcess: Process private val sourceFindingStrategies = ConcurrentHashMap() fun setupUtContext(classpathForUrlsClassloader: List) { - engineModel.setupUtContext.start(lifetime, SetupContextParams(classpathForUrlsClassloader)) + assertReadAccessNotAllowed() + engineModel.setupUtContext.startBlocking(SetupContextParams(classpathForUrlsClassloader)) } private fun computeSourceFileByClass(params: ComputeSourceFileByClassArguments): String = @@ -185,6 +187,8 @@ class EngineProcess private constructor(val project: Project, rdProcess: Process jdkInfo: JdkInfo, isCancelled: (Unit) -> Boolean ) { + assertReadAccessNotAllowed() + engineModel.isCancelled.set(handler = isCancelled) instrumenterAdapterModel.computeSourceFileByClass.set(handler = this::computeSourceFileByClass) @@ -194,19 +198,18 @@ class EngineProcess private constructor(val project: Project, rdProcess: Process dependencyPaths, JdkInfo(jdkInfo.path.pathString, jdkInfo.version) ) - engineModel.createTestGenerator.start(lifetime, params) + engineModel.createTestGenerator.startBlocking(params) } fun obtainClassId(canonicalName: String): ClassId { - assertIsNonDispatchThread() + assertReadAccessNotAllowed() return kryoHelper.readObject(engineModel.obtainClassId.startBlocking(canonicalName)) } fun findMethodsInClassMatchingSelected(clazzId: ClassId, srcMethods: List): List { - assertIsNonDispatchThread() - assertIsReadAccessAllowed() + assertReadAccessNotAllowed() - val srcDescriptions = srcMethods.map { it.methodDescription() } + val srcDescriptions = runReadAction { srcMethods.map { it.methodDescription() } } val rdDescriptions = srcDescriptions.map { MethodDescription(it.name, it.containingClass, it.parameterTypes) } val binaryClassId = kryoHelper.writeObject(clazzId) val arguments = FindMethodsInClassMatchingSelectedArguments(binaryClassId, rdDescriptions) @@ -216,10 +219,13 @@ class EngineProcess private constructor(val project: Project, rdProcess: Process } fun findMethodParamNames(classId: ClassId, methods: List): Map> { - assertIsNonDispatchThread() - assertIsReadAccessAllowed() + assertReadAccessNotAllowed() - val bySignature = methods.associate { it.methodDescription() to it.paramNames() } + val bySignature = executeWithTimeoutSuspended { + DumbService.getInstance(project).runReadActionInSmartMode(Computable { + methods.associate { it.methodDescription() to it.paramNames() } + }) + } val arguments = FindMethodParamNamesArguments( kryoHelper.writeObject(classId), kryoHelper.writeObject(bySignature) @@ -254,7 +260,7 @@ class EngineProcess private constructor(val project: Project, rdProcess: Process fuzzingValue: Double, searchDirectory: String ): RdTestGenerationResult { - assertIsNonDispatchThread() + assertReadAccessNotAllowed() val params = GenerateParams( mockInstalled, staticsMockingIsConfigured, @@ -291,7 +297,7 @@ class EngineProcess private constructor(val project: Project, rdProcess: Process enableTestsTimeout: Boolean, testClassPackageName: String ): Pair { - assertIsNonDispatchThread() + assertReadAccessNotAllowed() val params = RenderParams( testSetsId, kryoHelper.writeObject(classUnderTest), @@ -353,7 +359,7 @@ class EngineProcess private constructor(val project: Project, rdProcess: Process generatedTestsCode: String, sourceFindingStrategy: SourceFindingStrategy ): String { - assertIsNonDispatchThread() + assertReadAccessNotAllowed() val params = WriteSarifReportArguments(testSetsId, reportFilePath.pathString, generatedTestsCode) @@ -362,7 +368,7 @@ class EngineProcess private constructor(val project: Project, rdProcess: Process } fun generateTestsReport(model: GenerateTestsModel, eventLogMessage: String?): Triple { - assertIsNonDispatchThread() + assertReadAccessNotAllowed() val forceMockWarning = UtbotBundle.takeIf( "test.report.force.mock.warning", @@ -411,10 +417,12 @@ class EngineProcess private constructor(val project: Project, rdProcess: Process fun executeWithTimeoutSuspended(block: () -> T): T { try { + assertReadAccessNotAllowed() protocol.synchronizationModel.suspendTimeoutTimer.startBlocking(true) return block() } finally { + assertReadAccessNotAllowed() protocol.synchronizationModel.suspendTimeoutTimer.startBlocking(false) } } diff --git a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/util/IdeaThreadingUtil.kt b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/util/IdeaThreadingUtil.kt index 9cb1809199..526dde9bc6 100644 --- a/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/util/IdeaThreadingUtil.kt +++ b/utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/util/IdeaThreadingUtil.kt @@ -10,14 +10,18 @@ fun assertIsWriteThread() { ApplicationManager.getApplication().isWriteThread() } -fun assertIsReadAccessAllowed() { +fun assertReadAccessAllowed() { ApplicationManager.getApplication().assertReadAccessAllowed() } -fun assertIsWriteAccessAllowed() { +fun assertWriteAccessAllowed() { ApplicationManager.getApplication().assertWriteAccessAllowed() } fun assertIsNonDispatchThread() { ApplicationManager.getApplication().assertIsNonDispatchThread() +} + +fun assertReadAccessNotAllowed() { + ApplicationManager.getApplication().assertReadAccessNotAllowed() } \ No newline at end of file