Skip to content

Commit 47a0e3d

Browse files
IndexNotReadyException thrown in IDEA with installed UnitTestBot plug… (#1047)
IndexNotReadyException thrown in IDEA with installed UnitTestBot plugin #273 1) Add dumb mode wrapping 2) Split long EDT consumers into chains-of-invokeLater 3) Disable IntentionHelper for a while (it causes tricky exceptions and hanging)
1 parent b4d24b7 commit 47a0e3d

File tree

2 files changed

+100
-78
lines changed

2 files changed

+100
-78
lines changed

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

+85-62
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import com.intellij.openapi.command.WriteCommandAction.runWriteCommandAction
1313
import com.intellij.openapi.command.executeCommand
1414
import com.intellij.openapi.editor.Document
1515
import com.intellij.openapi.editor.Editor
16+
import com.intellij.openapi.fileEditor.FileDocumentManager
1617
import com.intellij.openapi.fileTypes.FileType
1718
import com.intellij.openapi.module.Module
1819
import com.intellij.openapi.project.DumbService
@@ -129,67 +130,87 @@ object CodeGenerationController {
129130
val testClass = createTestClass(srcClass, testDirectory, model) ?: continue
130131
val testFilePointer = SmartPointerManager.getInstance(model.project).createSmartPsiElementPointer(testClass.containingFile)
131132
val cut = psi2KClass[srcClass] ?: error("Didn't find KClass instance for class ${srcClass.name}")
132-
runWriteCommandAction(model.project, "Generate tests with UtBot", null, {
133-
try {
134-
generateCodeAndReport(srcClass, cut, testClass, testFilePointer, testSets, model, latch, reports, utilClassListener)
135-
testFilesPointers.add(testFilePointer)
136-
} catch (e: IncorrectOperationException) {
137-
logger.error { e }
138-
showCreatingClassError(model.project, createTestClassName(srcClass))
139-
}
140-
})
133+
run(EDT_LATER) {
134+
runWriteCommandAction(model.project, "Generate tests with UtBot", null, {
135+
try {
136+
generateCodeAndReport(
137+
srcClass,
138+
cut,
139+
testClass,
140+
testFilePointer,
141+
testSets,
142+
model,
143+
latch,
144+
reports,
145+
utilClassListener
146+
)
147+
testFilesPointers.add(testFilePointer)
148+
} catch (e: IncorrectOperationException) {
149+
logger.error { e }
150+
showCreatingClassError(model.project, createTestClassName(srcClass))
151+
}
152+
})
153+
}
141154
} catch (e: IncorrectOperationException) {
142155
logger.error { e }
143156
showCreatingClassError(model.project, createTestClassName(srcClass))
144157
}
145158
}
146159

147-
148-
run(EDT_LATER) {
149-
waitForCountDown(latch, timeout = 100, timeUnit = TimeUnit.MILLISECONDS) {
150-
val requiredUtilClassKind = utilClassListener.requiredUtilClassKind
151-
?: return@waitForCountDown // no util class needed
152-
153-
val existingUtilClass = model.codegenLanguage.getUtilClassOrNull(model.project, model.testModule)
154-
val utilClassKind = newUtilClassKindOrNull(existingUtilClass, requiredUtilClassKind)
155-
if (utilClassKind != null) {
156-
createOrUpdateUtilClass(
157-
testDirectory = baseTestDirectory,
158-
utilClassKind = utilClassKind,
159-
existingUtilClass = existingUtilClass,
160-
model = model
161-
)
162-
}
163-
}
164-
}
165-
166-
run(READ_ACTION) {
167-
val sarifReportsPath = model.testModule.getOrCreateSarifReportsPath(model.testSourceRoot)
168-
run(THREAD_POOL) {
169-
waitForCountDown(latch) {
170-
try {
171-
// Parametrized tests are not supported in tests report yet
172-
// TODO JIRA:1507
173-
if (model.parametrizedTestSource != ParametrizedTestSource.PARAMETRIZE) {
174-
showTestsReport(reports, model)
160+
run(THREAD_POOL) {
161+
waitForCountDown(latch) {
162+
run(EDT_LATER) {
163+
run(WRITE_ACTION) {
164+
createUtilityClassIfNeed(utilClassListener, model, baseTestDirectory)
165+
run(EDT_LATER) {
166+
try {
167+
// Parametrized tests are not supported in tests report yet
168+
// TODO JIRA:1507
169+
if (model.parametrizedTestSource != ParametrizedTestSource.PARAMETRIZE) {
170+
showTestsReport(reports, model)
171+
}
172+
} catch (e: Exception) {
173+
showErrorDialogLater(
174+
model.project,
175+
message = "Cannot save tests generation report: error occurred '${e.message}'",
176+
title = "Failed to save tests report"
177+
)
178+
}
179+
run(THREAD_POOL) {
180+
val sarifReportsPath =
181+
model.testModule.getOrCreateSarifReportsPath(model.testSourceRoot)
182+
mergeSarifReports(model, sarifReportsPath)
183+
if (model.runGeneratedTestsWithCoverage) {
184+
RunConfigurationHelper.runTestsWithCoverage(model, testFilesPointers)
185+
}
186+
}
175187
}
176-
} catch (e: Exception) {
177-
showErrorDialogLater(
178-
model.project,
179-
message = "Cannot save tests generation report: error occurred '${e.message}'",
180-
title = "Failed to save tests report"
181-
)
182-
}
183-
184-
mergeSarifReports(model, sarifReportsPath)
185-
if (model.runGeneratedTestsWithCoverage) {
186-
RunConfigurationHelper.runTestsWithCoverage(model, testFilesPointers)
187188
}
188189
}
189190
}
190191
}
191192
}
192193

194+
private fun createUtilityClassIfNeed(
195+
utilClassListener: UtilClassListener,
196+
model: GenerateTestsModel,
197+
baseTestDirectory: PsiDirectory
198+
) {
199+
val requiredUtilClassKind = utilClassListener.requiredUtilClassKind
200+
?: return // no util class needed
201+
202+
val existingUtilClass = model.codegenLanguage.getUtilClassOrNull(model.project, model.testModule)
203+
val utilClassKind = newUtilClassKindOrNull(existingUtilClass, requiredUtilClassKind)
204+
if (utilClassKind != null) {
205+
createOrUpdateUtilClass(
206+
testDirectory = baseTestDirectory,
207+
utilClassKind = utilClassKind,
208+
existingUtilClass = existingUtilClass,
209+
model = model
210+
)
211+
}
212+
}
213+
193214
/**
194215
* This method decides whether to overwrite an existing util class with a new one. And if so, then with what kind of util class.
195216
* - If no util class exists, then we generate a new one.
@@ -275,9 +296,9 @@ object CodeGenerationController {
275296
})
276297

277298
val utUtilsDocument = runReadAction {
278-
PsiDocumentManager
279-
.getInstance(model.project)
280-
.getDocument(utUtilsFile) ?: error("Failed to get a Document for UtUtils file")
299+
FileDocumentManager
300+
.getInstance()
301+
.getDocument(utUtilsFile.viewProvider.virtualFile) ?: error("Failed to get a Document for UtUtils file")
281302
}
282303

283304
unblockDocument(model.project, utUtilsDocument)
@@ -620,9 +641,9 @@ object CodeGenerationController {
620641
// reformatting before creating reports due to
621642
// SarifReport requires the final version of the generated tests code
622643
run(THREAD_POOL) {
623-
IntentionHelper(model.project, editor, filePointer).applyIntentions()
644+
// IntentionHelper(model.project, editor, filePointer).applyIntentions()
624645
run(EDT_LATER) {
625-
runWriteCommandAction(testClassUpdated.project, "UtBot tests reformatting", null, {
646+
runWriteCommandAction(filePointer.project, "UtBot tests reformatting", null, {
626647
reformat(model, filePointer, testClassUpdated)
627648
})
628649
unblockDocument(testClassUpdated.project, editor.document)
@@ -657,16 +678,18 @@ object CodeGenerationController {
657678
val project = model.project
658679
val codeStyleManager = CodeStyleManager.getInstance(project)
659680
val file = smartPointer.containingFile?: return
660-
codeStyleManager.reformat(file)
661-
when (model.codegenLanguage) {
662-
CodegenLanguage.JAVA -> {
663-
val range = file.textRange
664-
val startOffset = range.startOffset
665-
val endOffset = range.endOffset
666-
val reformatRange = codeStyleManager.reformatRange(file, startOffset, endOffset, false)
667-
JavaCodeStyleManager.getInstance(project).shortenClassReferences(reformatRange)
681+
DumbService.getInstance(model.project).runWhenSmart {
682+
codeStyleManager.reformat(file)
683+
when (model.codegenLanguage) {
684+
CodegenLanguage.JAVA -> {
685+
val range = file.textRange
686+
val startOffset = range.startOffset
687+
val endOffset = range.endOffset
688+
val reformatRange = codeStyleManager.reformatRange(file, startOffset, endOffset, false)
689+
JavaCodeStyleManager.getInstance(project).shortenClassReferences(reformatRange)
690+
}
691+
CodegenLanguage.KOTLIN -> ShortenReferences.DEFAULT.process((testClass as KtUltraLightClass).kotlinOrigin.containingKtFile)
668692
}
669-
CodegenLanguage.KOTLIN -> ShortenReferences.DEFAULT.process((testClass as KtUltraLightClass).kotlinOrigin.containingKtFile)
670693
}
671694
}
672695

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

+15-16
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@ import com.intellij.openapi.util.TextRange
1515
import com.intellij.psi.PsiFile
1616
import com.intellij.psi.SmartPsiElementPointer
1717
import mu.KotlinLogging
18-
import org.utbot.intellij.plugin.util.IntelliJApiHelper
19-
import org.utbot.intellij.plugin.util.IntelliJApiHelper.run
18+
import org.jetbrains.kotlin.idea.util.application.runReadAction
2019

2120
private val logger = KotlinLogging.logger {}
2221

@@ -52,30 +51,30 @@ class IntentionHelper(val project: Project, private val editor: Editor, private
5251
actions
5352
})
5453
actions.forEach {
55-
if (it.value.isApplicable()) {
54+
if (runReadAction {
55+
it.value.isApplicable() && it.key.isAvailable(
56+
project,
57+
editor,
58+
testFile.containingFile
59+
)
60+
}) {
5661
if (it.key.startInWriteAction()) {
5762
WriteCommandAction.runWriteCommandAction(project) {
58-
try {
59-
it.key.invoke(project, editor, testFile.containingFile)
60-
} catch (e: Exception) {
61-
logger.error { e }
62-
}
63+
invokeIntentionAction(it)
6364
}
6465
} else {
65-
run(IntelliJApiHelper.Target.EDT_LATER) {
66-
run(IntelliJApiHelper.Target.READ_ACTION) {
67-
try {
68-
it.key.invoke(project, editor, testFile.containingFile)
69-
} catch (e: Exception) {
70-
logger.error { e }
71-
}
72-
}
66+
runReadAction {
67+
invokeIntentionAction(it)
7368
}
7469
}
7570
}
7671
}
7772
}
7873

74+
private fun invokeIntentionAction(it: Map.Entry<IntentionAction, String>) {
75+
it.key.invoke(project, editor, testFile.containingFile)
76+
}
77+
7978
private fun String.isApplicable(): Boolean {
8079
if (this.startsWith("Change type of actual to ")) return true
8180
if (this == "Replace 'switch' with 'if'") return true // SetsTest

0 commit comments

Comments
 (0)