Skip to content

Commit 88f83c0

Browse files
Plugin Action stays locked even when previous generation is finished … (#1175)
1 parent fb0196e commit 88f83c0

File tree

3 files changed

+131
-127
lines changed

3 files changed

+131
-127
lines changed

utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/Lock.kt renamed to utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/LockFile.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,15 @@ private val lockFilePath = "$utbotHomePath/utbot.lock"
1313
private var currentLock : OutputStream? = null
1414
private val logger = KotlinLogging.logger {}
1515

16-
object Lock {
16+
object LockFile {
1717
@Synchronized
1818
fun isLocked() = currentLock != null
1919

2020
@Synchronized
2121
fun lock(): Boolean {
2222
if (currentLock != null) return false
2323
return try {
24+
Paths.get(utbotHomePath).toFile().mkdirs()
2425
currentLock = Paths.get(lockFilePath).outputStream(StandardOpenOption.CREATE_NEW, StandardOpenOption.DELETE_ON_CLOSE).also {
2526
it.write(DateFormat.getDateTimeInstance().format(System.currentTimeMillis()).toByteArray())
2627
}

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

Lines changed: 127 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ import java.nio.file.Path
4848
import java.nio.file.Paths
4949
import java.util.concurrent.TimeUnit
5050
import kotlin.io.path.pathString
51-
import org.utbot.framework.plugin.api.util.Lock
51+
import org.utbot.framework.plugin.api.util.LockFile
5252

5353
object UtTestsDialogProcessor {
5454
private val logger = KotlinLogging.logger {}
@@ -122,150 +122,153 @@ object UtTestsDialogProcessor {
122122
promise.onSuccess {
123123
if (it.hasErrors() || it.isAborted)
124124
return@onSuccess
125-
if (!Lock.lock()) {
126-
return@onSuccess
127-
}
128125

129126
(object : Task.Backgroundable(project, "Generate tests") {
130127

131128
override fun run(indicator: ProgressIndicator) {
132-
val ldef = LifetimeDefinition()
133-
ldef.onTermination { Lock.unlock() }
134-
ldef.terminateOnException { lifetime ->
135-
val secondsTimeout = TimeUnit.MILLISECONDS.toSeconds(model.timeout)
136-
137-
indicator.isIndeterminate = false
138-
updateIndicator(indicator, ProgressRange.SOLVING, "Generate tests: read classes", 0.0)
139-
140-
val buildPaths = ReadAction
141-
.nonBlocking<BuildPaths?> { findPaths(model.srcClasses) }
142-
.executeSynchronously()
143-
?: return
144-
145-
val (buildDirs, classpath, classpathList, pluginJarsPath) = buildPaths
146-
147-
val testSetsByClass = mutableMapOf<PsiClass, RdTestGenerationResult>()
148-
val psi2KClass = mutableMapOf<PsiClass, ClassId>()
149-
var processedClasses = 0
150-
val totalClasses = model.srcClasses.size
151-
152-
val proc = EngineProcess(lifetime, project)
153-
154-
proc.setupUtContext(buildDirs + classpathList)
155-
proc.createTestGenerator(
156-
buildDirs,
157-
classpath,
158-
pluginJarsPath.joinToString(separator = File.pathSeparator),
159-
JdkInfoService.provide()
160-
) {
161-
ApplicationManager.getApplication().runReadAction(Computable {
162-
indicator.isCanceled
163-
})
164-
}
129+
if (!LockFile.lock()) {
130+
return
131+
}
132+
try {
133+
val ldef = LifetimeDefinition()
134+
ldef.terminateOnException { lifetime ->
135+
val secondsTimeout = TimeUnit.MILLISECONDS.toSeconds(model.timeout)
165136

166-
for (srcClass in model.srcClasses) {
167-
val (methods, className) = DumbService.getInstance(project)
168-
.runReadActionInSmartMode(Computable {
169-
val canonicalName = srcClass.canonicalName
170-
val classId = proc.obtainClassId(canonicalName)
171-
psi2KClass[srcClass] = classId
172-
173-
val srcMethods = if (model.extractMembersFromSrcClasses) {
174-
val chosenMethods = model.selectedMembers.filter { it.member is PsiMethod }
175-
val chosenNestedClasses =
176-
model.selectedMembers.mapNotNull { it.member as? PsiClass }
177-
chosenMethods + chosenNestedClasses.flatMap {
178-
it.extractClassMethodsIncludingNested(false)
179-
}
180-
} else {
181-
srcClass.extractClassMethodsIncludingNested(false)
182-
}
183-
proc.findMethodsInClassMatchingSelected(classId, srcMethods) to srcClass.name
184-
})
137+
indicator.isIndeterminate = false
138+
updateIndicator(indicator, ProgressRange.SOLVING, "Generate tests: read classes", 0.0)
185139

186-
if (methods.isEmpty()) {
187-
logger.error { "No methods matching selected found in class $className." }
188-
continue
140+
val buildPaths = ReadAction
141+
.nonBlocking<BuildPaths?> { findPaths(model.srcClasses) }
142+
.executeSynchronously()
143+
?: return
144+
145+
val (buildDirs, classpath, classpathList, pluginJarsPath) = buildPaths
146+
147+
val testSetsByClass = mutableMapOf<PsiClass, RdTestGenerationResult>()
148+
val psi2KClass = mutableMapOf<PsiClass, ClassId>()
149+
var processedClasses = 0
150+
val totalClasses = model.srcClasses.size
151+
152+
val proc = EngineProcess(lifetime, project)
153+
154+
proc.setupUtContext(buildDirs + classpathList)
155+
proc.createTestGenerator(
156+
buildDirs,
157+
classpath,
158+
pluginJarsPath.joinToString(separator = File.pathSeparator),
159+
JdkInfoService.provide()
160+
) {
161+
ApplicationManager.getApplication().runReadAction(Computable {
162+
indicator.isCanceled
163+
})
189164
}
190165

191-
if (totalClasses > 1) {
192-
updateIndicator(
193-
indicator,
194-
ProgressRange.SOLVING,
195-
"Generate test cases for class $className",
196-
processedClasses.toDouble() / totalClasses
197-
)
198-
}
166+
for (srcClass in model.srcClasses) {
167+
val (methods, className) = DumbService.getInstance(project)
168+
.runReadActionInSmartMode(Computable {
169+
val canonicalName = srcClass.canonicalName
170+
val classId = proc.obtainClassId(canonicalName)
171+
psi2KClass[srcClass] = classId
172+
173+
val srcMethods = if (model.extractMembersFromSrcClasses) {
174+
val chosenMethods = model.selectedMembers.filter { it.member is PsiMethod }
175+
val chosenNestedClasses =
176+
model.selectedMembers.mapNotNull { it.member as? PsiClass }
177+
chosenMethods + chosenNestedClasses.flatMap {
178+
it.extractClassMethodsIncludingNested(false)
179+
}
180+
} else {
181+
srcClass.extractClassMethodsIncludingNested(false)
182+
}
183+
proc.findMethodsInClassMatchingSelected(classId, srcMethods) to srcClass.name
184+
})
199185

200-
// set timeout for concrete execution and for generated tests
201-
UtSettings.concreteExecutionTimeoutInChildProcess =
202-
model.hangingTestsTimeout.timeoutMs
186+
if (methods.isEmpty()) {
187+
logger.error { "No methods matching selected found in class $className." }
188+
continue
189+
}
203190

204-
UtSettings.useCustomJavaDocTags =
205-
model.commentStyle == JavaDocCommentStyle.CUSTOM_JAVADOC_TAGS
191+
if (totalClasses > 1) {
192+
updateIndicator(
193+
indicator,
194+
ProgressRange.SOLVING,
195+
"Generate test cases for class $className",
196+
processedClasses.toDouble() / totalClasses
197+
)
198+
}
206199

207-
UtSettings.enableSummariesGeneration = model.enableSummariesGeneration
200+
// set timeout for concrete execution and for generated tests
201+
UtSettings.concreteExecutionTimeoutInChildProcess =
202+
model.hangingTestsTimeout.timeoutMs
208203

209-
val searchDirectory = ReadAction
210-
.nonBlocking<Path> {
211-
project.basePath?.let { Paths.get(it) }
212-
?: Paths.get(srcClass.containingFile.virtualFile.parent.path)
213-
}
214-
.executeSynchronously()
204+
UtSettings.useCustomJavaDocTags =
205+
model.commentStyle == JavaDocCommentStyle.CUSTOM_JAVADOC_TAGS
215206

216-
withStaticsSubstitutionRequired(true) {
217-
val mockFrameworkInstalled = model.mockFramework?.isInstalled ?: true
218-
219-
val rdGenerateResult = proc.generate(
220-
mockFrameworkInstalled,
221-
model.staticsMocking.isConfigured,
222-
model.conflictTriggers,
223-
methods,
224-
model.mockStrategy,
225-
model.chosenClassesToMockAlways,
226-
model.timeout,
227-
model.timeout,
228-
true,
229-
UtSettings.useFuzzing,
230-
project.service<Settings>().fuzzingValue,
231-
searchDirectory.pathString
232-
)
233-
234-
if (rdGenerateResult.notEmptyCases == 0) {
235-
if (model.srcClasses.size > 1) {
236-
logger.error { "Failed to generate any tests cases for class $className" }
207+
UtSettings.enableSummariesGeneration = model.enableSummariesGeneration
208+
209+
val searchDirectory = ReadAction
210+
.nonBlocking<Path> {
211+
project.basePath?.let { Paths.get(it) }
212+
?: Paths.get(srcClass.containingFile.virtualFile.parent.path)
213+
}
214+
.executeSynchronously()
215+
216+
withStaticsSubstitutionRequired(true) {
217+
val mockFrameworkInstalled = model.mockFramework?.isInstalled ?: true
218+
219+
val rdGenerateResult = proc.generate(
220+
mockFrameworkInstalled,
221+
model.staticsMocking.isConfigured,
222+
model.conflictTriggers,
223+
methods,
224+
model.mockStrategy,
225+
model.chosenClassesToMockAlways,
226+
model.timeout,
227+
model.timeout,
228+
true,
229+
UtSettings.useFuzzing,
230+
project.service<Settings>().fuzzingValue,
231+
searchDirectory.pathString
232+
)
233+
234+
if (rdGenerateResult.notEmptyCases == 0) {
235+
if (model.srcClasses.size > 1) {
236+
logger.error { "Failed to generate any tests cases for class $className" }
237+
} else {
238+
showErrorDialogLater(
239+
model.project,
240+
errorMessage(className, secondsTimeout),
241+
title = "Failed to generate unit tests for class $className"
242+
)
243+
}
237244
} else {
238-
showErrorDialogLater(
239-
model.project,
240-
errorMessage(className, secondsTimeout),
241-
title = "Failed to generate unit tests for class $className"
242-
)
245+
testSetsByClass[srcClass] = rdGenerateResult
243246
}
244-
} else {
245-
testSetsByClass[srcClass] = rdGenerateResult
246247
}
248+
processedClasses++
247249
}
248-
processedClasses++
249-
}
250250

251-
if (processedClasses == 0) {
252-
invokeLater {
253-
Messages.showInfoMessage(
254-
model.project,
255-
"No methods for test generation were found among selected items",
256-
"No methods found"
257-
)
251+
if (processedClasses == 0) {
252+
invokeLater {
253+
Messages.showInfoMessage(
254+
model.project,
255+
"No methods for test generation were found among selected items",
256+
"No methods found"
257+
)
258+
}
259+
return
258260
}
259-
return
260-
}
261-
updateIndicator(indicator, ProgressRange.CODEGEN, "Generate code for tests", 0.0)
262-
// Commented out to generate tests for collected executions even if action was canceled.
263-
// indicator.checkCanceled()
261+
updateIndicator(indicator, ProgressRange.CODEGEN, "Generate code for tests", 0.0)
262+
// Commented out to generate tests for collected executions even if action was canceled.
263+
// indicator.checkCanceled()
264264

265-
invokeLater {
266-
generateTests(model, testSetsByClass, psi2KClass, proc, indicator)
267-
logger.info { "Generation complete" }
265+
invokeLater {
266+
generateTests(model, testSetsByClass, psi2KClass, proc, indicator)
267+
logger.info { "Generation complete" }
268+
}
268269
}
270+
} finally {
271+
LockFile.unlock()
269272
}
270273
}
271274
}).queue()

utbot-intellij/src/main/kotlin/org/utbot/intellij/plugin/ui/actions/GenerateTestsAction.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import org.utbot.intellij.plugin.util.isVisible
2626
import java.util.*
2727
import org.jetbrains.kotlin.j2k.getContainingClass
2828
import org.jetbrains.kotlin.utils.addIfNotNull
29-
import org.utbot.framework.plugin.api.util.Lock
29+
import org.utbot.framework.plugin.api.util.LockFile
3030
import org.utbot.intellij.plugin.models.packageName
3131
import org.utbot.intellij.plugin.ui.InvalidClassNotifier
3232
import org.utbot.intellij.plugin.util.isAbstract
@@ -42,7 +42,7 @@ class GenerateTestsAction : AnAction(), UpdateInBackground {
4242
}
4343

4444
override fun update(e: AnActionEvent) {
45-
if (Lock.isLocked()) {
45+
if (LockFile.isLocked()) {
4646
e.presentation.isEnabled = false
4747
return
4848
}

0 commit comments

Comments
 (0)