Skip to content

Commit 7f8ab19

Browse files
IDE fatal error during tests generation #68
1 parent d4f95c1 commit 7f8ab19

File tree

1 file changed

+112
-54
lines changed
  • utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/generator

1 file changed

+112
-54
lines changed

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

Lines changed: 112 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,28 @@
11
package org.utbot.intellij.plugin.generator
22

3+
import org.utbot.common.HTML_LINE_SEPARATOR
4+
import org.utbot.common.PathUtil.classFqnToPath
5+
import org.utbot.common.PathUtil.toHtmlLinkTag
6+
import org.utbot.common.appendHtmlLine
7+
import org.utbot.framework.codegen.Import
8+
import org.utbot.framework.codegen.ParametrizedTestSource
9+
import org.utbot.framework.codegen.StaticImport
10+
import org.utbot.framework.codegen.TestsCodeWithTestReport
11+
import org.utbot.framework.codegen.model.ModelBasedTestCodeGenerator
12+
import org.utbot.framework.codegen.model.constructor.tree.TestsGenerationReport
13+
import org.utbot.framework.plugin.api.CodegenLanguage
14+
import org.utbot.framework.plugin.api.UtTestCase
15+
import org.utbot.intellij.plugin.sarif.SarifReportIdea
16+
import org.utbot.intellij.plugin.sarif.SourceFindingStrategyIdea
17+
import org.utbot.intellij.plugin.settings.Settings
18+
import org.utbot.intellij.plugin.ui.utils.getOrCreateSarifReportsPath
19+
import org.utbot.intellij.plugin.ui.utils.getOrCreateTestResourcesPath
20+
import org.utbot.sarif.SarifReport
321
import com.intellij.codeInsight.CodeInsightUtil
422
import com.intellij.codeInsight.FileModificationService
523
import com.intellij.ide.fileTemplates.FileTemplateManager
624
import com.intellij.ide.fileTemplates.FileTemplateUtil
725
import com.intellij.ide.fileTemplates.JavaTemplateUtil
8-
import com.intellij.openapi.application.ApplicationManager
926
import com.intellij.openapi.command.WriteCommandAction.runWriteCommandAction
1027
import com.intellij.openapi.command.executeCommand
1128
import com.intellij.openapi.components.service
@@ -21,51 +38,54 @@ import com.intellij.psi.codeStyle.JavaCodeStyleManager
2138
import com.intellij.psi.search.GlobalSearchScopesCore
2239
import com.intellij.testIntegration.TestIntegrationUtils
2340
import com.intellij.util.IncorrectOperationException
41+
import com.intellij.util.concurrency.AppExecutorUtil
2442
import com.intellij.util.io.exists
2543
import com.siyeh.ig.psiutils.ImportUtils
44+
import java.nio.file.Paths
45+
import java.util.concurrent.CountDownLatch
2646
import org.jetbrains.kotlin.asJava.classes.KtUltraLightClass
2747
import org.jetbrains.kotlin.idea.core.ShortenReferences
2848
import org.jetbrains.kotlin.idea.core.getPackage
2949
import org.jetbrains.kotlin.idea.core.util.toPsiDirectory
3050
import org.jetbrains.kotlin.idea.util.ImportInsertHelperImpl
51+
import org.jetbrains.kotlin.idea.util.application.invokeLater
3152
import org.jetbrains.kotlin.name.FqName
3253
import org.jetbrains.kotlin.psi.KtClass
3354
import org.jetbrains.kotlin.psi.KtNamedFunction
3455
import org.jetbrains.kotlin.psi.KtPsiFactory
3556
import org.jetbrains.kotlin.psi.psiUtil.endOffset
3657
import org.jetbrains.kotlin.psi.psiUtil.startOffset
3758
import org.jetbrains.kotlin.scripting.resolve.classId
38-
import org.utbot.common.HTML_LINE_SEPARATOR
39-
import org.utbot.common.PathUtil.classFqnToPath
40-
import org.utbot.common.PathUtil.toHtmlLinkTag
41-
import org.utbot.common.appendHtmlLine
42-
import org.utbot.framework.codegen.Import
43-
import org.utbot.framework.codegen.ParametrizedTestSource
44-
import org.utbot.framework.codegen.StaticImport
45-
import org.utbot.framework.codegen.TestsCodeWithTestReport
46-
import org.utbot.framework.codegen.model.ModelBasedTestCodeGenerator
47-
import org.utbot.framework.codegen.model.constructor.tree.TestsGenerationReport
48-
import org.utbot.framework.plugin.api.CodegenLanguage
49-
import org.utbot.framework.plugin.api.UtTestCase
5059
import org.utbot.intellij.plugin.error.showErrorDialogLater
51-
import org.utbot.intellij.plugin.sarif.SarifReportIdea
52-
import org.utbot.intellij.plugin.sarif.SourceFindingStrategyIdea
53-
import org.utbot.intellij.plugin.settings.Settings
54-
import org.utbot.intellij.plugin.ui.*
55-
import org.utbot.intellij.plugin.ui.utils.getOrCreateSarifReportsPath
56-
import org.utbot.intellij.plugin.ui.utils.getOrCreateTestResourcesPath
57-
import org.utbot.sarif.SarifReport
58-
import java.nio.file.Paths
60+
import org.utbot.intellij.plugin.ui.GenerateTestsModel
61+
import org.utbot.intellij.plugin.ui.SarifReportNotifier
62+
import org.utbot.intellij.plugin.ui.TestReportUrlOpeningListener
63+
import org.utbot.intellij.plugin.ui.TestsReportNotifier
64+
import org.utbot.intellij.plugin.ui.packageName
5965

6066
object TestGenerator {
61-
fun generateTests(model: GenerateTestsModel, testCases: Map<PsiClass, List<UtTestCase>>) {
62-
generateTestsInternal(model, testCases)
67+
private enum class Target {THREAD_POOL, READ_ACTION, WRITE_ACTION, EDT_LATER}
68+
69+
private fun run(target: Target, runnable: Runnable) {
70+
UtContext.currentContext()?.let {
71+
when (target) {
72+
THREAD_POOL -> AppExecutorUtil.getAppExecutorService().submit {
73+
withUtContext(it) {
74+
runnable.run()
75+
}
76+
}
77+
READ_ACTION -> runReadAction { withUtContext(it) { runnable.run() } }
78+
WRITE_ACTION -> runWriteAction { withUtContext(it) { runnable.run() } }
79+
EDT_LATER -> invokeLater { withUtContext(it) { runnable.run() } }
80+
}
81+
} ?: error("No context in thread ${Thread.currentThread()}")
6382
}
6483

65-
private fun generateTestsInternal(model: GenerateTestsModel, testCasesByClass: Map<PsiClass, List<UtTestCase>>) {
84+
fun generateTests(model: GenerateTestsModel, testCasesByClass: Map<PsiClass, List<UtTestCase>>) {
6685
val baseTestDirectory = model.testSourceRoot?.toPsiDirectory(model.project)
6786
?: return
6887
val allTestPackages = getPackageDirectories(baseTestDirectory)
88+
val latch = CountDownLatch(testCasesByClass.size)
6989

7090
for (srcClass in testCasesByClass.keys) {
7191
val testCases = testCasesByClass[srcClass] ?: continue
@@ -75,20 +95,37 @@ object TestGenerator {
7595
val testDirectory = allTestPackages[classPackageName] ?: baseTestDirectory
7696
val testClass = createTestClass(srcClass, testDirectory, model) ?: continue
7797
val file = testClass.containingFile
78-
7998
runWriteCommandAction(model.project, "Generate tests with UtBot", null, {
80-
addTestMethodsAndSaveReports(testClass, file, testCases, model)
99+
try {
100+
addTestMethodsAndSaveReports(testClass, file, testCases, model, latch)
101+
} catch (e: IncorrectOperationException) {
102+
showCreatingClassError(model.project, createTestClassName(srcClass))
103+
}
81104
})
82105
} catch (e: IncorrectOperationException) {
83106
showCreatingClassError(model.project, createTestClassName(srcClass))
84107
}
85108
}
109+
run(Target.READ_ACTION) {
110+
val sarifReportsPath = model.testModule.getOrCreateSarifReportsPath(model.testSourceRoot)
111+
run(Target.THREAD_POOL) {
112+
waitForCountDown(latch, model, sarifReportsPath)
113+
}
114+
}
115+
}
86116

87-
mergeSarifReports(model)
117+
private fun waitForCountDown(latch: CountDownLatch, model: GenerateTestsModel, sarifReportsPath : Path) {
118+
try {
119+
if (!latch.await(5, TimeUnit.SECONDS)) {
120+
run(THREAD_POOL) { waitForCountDown(latch, model, sarifReportsPath) }
121+
} else {
122+
mergeSarifReports(model, sarifReportsPath)
123+
}
124+
} catch (ignored: InterruptedException) {
125+
}
88126
}
89127

90-
private fun mergeSarifReports(model: GenerateTestsModel) {
91-
val sarifReportsPath = model.testModule.getOrCreateSarifReportsPath(model.testSourceRoot)
128+
private fun mergeSarifReports(model: GenerateTestsModel, sarifReportsPath : Path) {
92129
val sarifReports = sarifReportsPath.toFile()
93130
.walkTopDown()
94131
.filter { it.extension == "sarif" }
@@ -175,6 +212,7 @@ object TestGenerator {
175212
file: PsiFile,
176213
testCases: List<UtTestCase>,
177214
model: GenerateTestsModel,
215+
reportsCountDown: CountDownLatch,
178216
) {
179217
val selectedMethods = TestIntegrationUtils.extractClassMethods(testClass, false)
180218
val testFramework = model.testFramework
@@ -205,33 +243,50 @@ object TestGenerator {
205243
when (generator) {
206244
is ModelBasedTestCodeGenerator -> {
207245
val editor = CodeInsightUtil.positionCursorAtLBrace(testClass.project, file, testClass)
208-
val testsCodeWithTestReport = generator.generateAsStringWithTestReport(testCases)
209-
val generatedTestsCode = testsCodeWithTestReport.generatedCode
246+
//TODO Use PsiDocumentManager.getInstance(model.project).getDocument(file)
247+
// if we don't want to open _all_ new files with tests in editor one-by-one
248+
run(THREAD_POOL) {
249+
val testsCodeWithTestReport = generator.generateAsStringWithTestReport(testCases)
250+
val generatedTestsCode = testsCodeWithTestReport.generatedCode
251+
run(EDT_LATER) {
252+
run(WRITE_ACTION) {
253+
unblockDocument(testClass.project, editor.document)
254+
// TODO: JIRA:1246 - display warnings if we rewrite the file
255+
executeCommand(testClass.project, "Insert Generated Tests") {
256+
editor.document.setText(generatedTestsCode)
257+
}
258+
unblockDocument(testClass.project, editor.document)
259+
260+
// after committing the document the `testClass` is invalid in PsiTree,
261+
// so we have to reload it from the corresponding `file`
262+
val testClassUpdated = (file as PsiClassOwner).classes.first() // only one class in the file
263+
264+
// reformatting before creating reports due to
265+
// SarifReport requires the final version of the generated tests code
266+
runWriteCommandAction(testClassUpdated.project, "UtBot tests reformatting", null, {
267+
reformat(model, file, testClassUpdated)
268+
})
269+
unblockDocument(testClassUpdated.project, editor.document)
270+
271+
// uploading formatted code
272+
val testsCodeWithTestReportFormatted =
273+
testsCodeWithTestReport.copy(generatedCode = file.text)
274+
275+
// creating and saving reports
276+
saveSarifAndTestReports(
277+
testClassUpdated,
278+
testCases,
279+
model,
280+
testsCodeWithTestReportFormatted,
281+
reportsCountDown
282+
)
210283

211-
unblockDocument(testClass.project, editor.document)
212-
// TODO: JIRA:1246 - display warnings if we rewrite the file
213-
executeCommand(testClass.project, "Insert Generated Tests") {
214-
editor.document.setText(generatedTestsCode)
284+
unblockDocument(testClassUpdated.project, editor.document)
285+
}
286+
}
215287
}
216-
unblockDocument(testClass.project, editor.document)
217-
218-
// after committing the document the `testClass` is invalid in PsiTree,
219-
// so we have to reload it from the corresponding `file`
220-
val testClassUpdated = (file as PsiClassOwner).classes.first() // only one class in the file
221-
222-
// reformatting before creating reports due to
223-
// SarifReport requires the final version of the generated tests code
224-
reformat(model, file, testClassUpdated)
225-
unblockDocument(testClassUpdated.project, editor.document)
226-
227-
// uploading formatted code
228-
val testsCodeWithTestReportFormatted = testsCodeWithTestReport.copy(generatedCode = file.text)
229-
230-
// creating and saving reports
231-
saveSarifAndTestReports(testClassUpdated, testCases, model, testsCodeWithTestReportFormatted)
232-
233-
unblockDocument(testClassUpdated.project, editor.document)
234288
}
289+
//Note that reportsCountDown.countDown() has to be called in every generator implementation to complete whole process
235290
else -> TODO("Only model based code generator supported, but got: ${generator::class}")
236291
}
237292
}
@@ -257,7 +312,8 @@ object TestGenerator {
257312
testClass: PsiClass,
258313
testCases: List<UtTestCase>,
259314
model: GenerateTestsModel,
260-
testsCodeWithTestReport: TestsCodeWithTestReport
315+
testsCodeWithTestReport: TestsCodeWithTestReport,
316+
reportsCountDown: CountDownLatch
261317
) {
262318
val project = model.project
263319
val generatedTestsCode = testsCodeWithTestReport.generatedCode
@@ -274,6 +330,8 @@ object TestGenerator {
274330
message = "Cannot save Sarif report via generated tests: error occurred '${e.message}'",
275331
title = "Failed to save Sarif report"
276332
)
333+
} finally {
334+
reportsCountDown.countDown()
277335
}
278336

279337
try {

0 commit comments

Comments
 (0)