@@ -98,19 +98,9 @@ object CodeGenerationController {
98
98
99
99
private class UtilClassListener {
100
100
var requiredUtilClassKind: UtilClassKind ? = null
101
- var mockFrameworkUsed: Boolean = false
102
101
103
102
fun onTestClassGenerated (result : CodeGeneratorResult ) {
104
103
requiredUtilClassKind = maxOfNullable(requiredUtilClassKind, result.utilClassKind)
105
- mockFrameworkUsed = maxOf(mockFrameworkUsed, result.mockFrameworkUsed)
106
- }
107
-
108
- private fun <T : Comparable <T >> maxOfNullable (a : T ? , b : T ? ): T ? {
109
- return when {
110
- a == null -> b
111
- b == null -> a
112
- else -> maxOf(a, b)
113
- }
114
104
}
115
105
}
116
106
@@ -161,12 +151,12 @@ object CodeGenerationController {
161
151
162
152
run (EDT_LATER ) {
163
153
waitForCountDown(latch, timeout = 100 , timeUnit = TimeUnit .MILLISECONDS ) {
164
- val mockFrameworkUsed = utilClassListener.mockFrameworkUsed
165
- val utilClassKind = utilClassListener.requiredUtilClassKind
154
+ val requiredUtilClassKind = utilClassListener.requiredUtilClassKind
166
155
? : return @waitForCountDown // no util class needed
167
156
168
157
val existingUtilClass = model.codegenLanguage.getUtilClassOrNull(model.project, model.testModule)
169
- if (shouldCreateOrUpdateUtilClass(existingUtilClass, mockFrameworkUsed, utilClassKind)) {
158
+ val utilClassKind = newUtilClassKindOrNull(existingUtilClass, requiredUtilClassKind)
159
+ if (utilClassKind != null ) {
170
160
createOrUpdateUtilClass(
171
161
testDirectory = baseTestDirectory,
172
162
utilClassKind = utilClassKind,
@@ -204,40 +194,50 @@ object CodeGenerationController {
204
194
}
205
195
}
206
196
207
- private fun shouldCreateOrUpdateUtilClass (
208
- existingUtilClass : PsiFile ? ,
209
- mockFrameworkUsed : Boolean ,
210
- requiredUtilClassKind : UtilClassKind
211
- ): Boolean {
212
- val mockFrameworkNotUsed = ! mockFrameworkUsed
213
-
214
- val utilClassExists = existingUtilClass != null
215
-
216
- if (! utilClassExists) {
217
- // If no util class exists, then we should create a new one.
218
- return true
197
+ /* *
198
+ * This method decides whether to overwrite an existing util class with a new one. And if so, then with what kind of util class.
199
+ * - If no util class exists, then we generate a new one.
200
+ * - If existing util class' version is out of date, then we overwrite it with a new one.
201
+ * But we use the maximum of two kinds (existing and the new one) to avoid problems with mocks.
202
+ * - If existing util class is up-to-date **and** has a greater or equal priority than the new one,
203
+ * then we do not need to overwrite it (return null).
204
+ * - Lastly, if the new util class kind has a greater priority than the existing one,
205
+ * then we do overwrite it with a newer version.
206
+ *
207
+ * @param existingUtilClass a [PsiFile] representing a file of an existing util class. If it does not exist, then [existingUtilClass] is `null`.
208
+ * @param requiredUtilClassKind the kind of the new util class that we attempt to generate.
209
+ * @return an [UtilClassKind] of a new util class that will be created or `null`, if no new util class is needed.
210
+ */
211
+ private fun newUtilClassKindOrNull (existingUtilClass : PsiFile ? , requiredUtilClassKind : UtilClassKind ): UtilClassKind ? {
212
+ if (existingUtilClass == null ) {
213
+ // If no util class exists, then we should create a new one with the given kind.
214
+ return requiredUtilClassKind
219
215
}
220
216
221
- val existingUtilClassVersion = existingUtilClass? .utilClassVersionOrNull
217
+ val existingUtilClassVersion = existingUtilClass.utilClassVersionOrNull ? : return requiredUtilClassKind
222
218
val newUtilClassVersion = requiredUtilClassKind.utilClassVersion
223
219
val versionIsUpdated = existingUtilClassVersion != newUtilClassVersion
224
220
221
+ val existingUtilClassKind = existingUtilClass.utilClassKindOrNull ? : return requiredUtilClassKind
222
+
225
223
if (versionIsUpdated) {
226
- // If an existing util class is out of date,
227
- // then we must overwrite it with a newer version.
228
- return true
224
+ // If an existing util class is out of date, then we must overwrite it with a newer version.
225
+ // But we choose the kind with more priority, because it is possible that
226
+ // the existing util class needed mocks, but the new one doesn't.
227
+ // In this case we still want to support mocks, because the previously generated tests
228
+ // expect that the util class does support them.
229
+ return maxOfNullable(existingUtilClassKind, requiredUtilClassKind)
229
230
}
230
231
231
- if (mockFrameworkNotUsed) {
232
- // If util class already exists and mock framework is not used,
233
- // then existing util class is enough, and we don't need to generate a new one.
234
- // That's because both regular and mock versions of util class can work
235
- // with tests that do not use mocks, so we do not have to worry about
236
- // version of util class that we have at the moment.
237
- return false
232
+ if (requiredUtilClassKind <= existingUtilClassKind) {
233
+ // If the existing util class kind has a greater or equal priority than the new one we attempt to generate,
234
+ // then we should not do anything. The existing util class is already enough.
235
+ return null
238
236
}
239
237
240
- return true
238
+ // The last case. The existing util class has a strictly less priority than the new one.
239
+ // So we generate the new one to overwrite the previous one with it.
240
+ return requiredUtilClassKind
241
241
}
242
242
243
243
/* *
@@ -345,7 +345,7 @@ object CodeGenerationController {
345
345
}
346
346
347
347
/* *
348
- * Util class must have a comment that specifies the version of UTBot it was generated with .
348
+ * Util class must have a comment that specifies its version.
349
349
* This property represents the version specified by this comment if it exists. Otherwise, the property is `null`.
350
350
*/
351
351
private val PsiFile .utilClassVersionOrNull: String?
@@ -362,6 +362,23 @@ object CodeGenerationController {
362
362
?.trim()
363
363
}
364
364
365
+ /* *
366
+ * Util class must have a comment that specifies its kind.
367
+ * This property obtains the kind specified by this comment if it exists. Otherwise, the property is `null`.
368
+ */
369
+ private val PsiFile .utilClassKindOrNull: UtilClassKind ?
370
+ get() = runReadAction {
371
+ val utilClass = (this as ? PsiClassOwner )
372
+ ?.classes
373
+ ?.firstOrNull()
374
+ ? : return @runReadAction null
375
+
376
+ utilClass.childrenOfType<PsiComment >()
377
+ .map { comment -> comment.text }
378
+ .mapNotNull { text -> UtilClassKind .utilClassKindByCommentOrNull(text) }
379
+ .firstOrNull()
380
+ }
381
+
365
382
/* *
366
383
* @param srcClass class under test
367
384
* @return name of the package of a given [srcClass].
@@ -886,4 +903,12 @@ object CodeGenerationController {
886
903
title = " Failed to Create Class"
887
904
)
888
905
}
906
+
907
+ private fun <T : Comparable <T >> maxOfNullable (a : T ? , b : T ? ): T ? {
908
+ return when {
909
+ a == null -> b
910
+ b == null -> a
911
+ else -> maxOf(a, b)
912
+ }
913
+ }
889
914
}
0 commit comments