@@ -96,19 +96,9 @@ object CodeGenerationController {
96
96
97
97
private class UtilClassListener {
98
98
var requiredUtilClassKind: UtilClassKind ? = null
99
- var mockFrameworkUsed: Boolean = false
100
99
101
100
fun onTestClassGenerated (result : CodeGeneratorResult ) {
102
101
requiredUtilClassKind = maxOfNullable(requiredUtilClassKind, result.utilClassKind)
103
- mockFrameworkUsed = maxOf(mockFrameworkUsed, result.mockFrameworkUsed)
104
- }
105
-
106
- private fun <T : Comparable <T >> maxOfNullable (a : T ? , b : T ? ): T ? {
107
- return when {
108
- a == null -> b
109
- b == null -> a
110
- else -> maxOf(a, b)
111
- }
112
102
}
113
103
}
114
104
@@ -153,12 +143,12 @@ object CodeGenerationController {
153
143
154
144
run (EDT_LATER ) {
155
145
waitForCountDown(latch, timeout = 100 , timeUnit = TimeUnit .MILLISECONDS ) {
156
- val mockFrameworkUsed = utilClassListener.mockFrameworkUsed
157
- val utilClassKind = utilClassListener.requiredUtilClassKind
146
+ val requiredUtilClassKind = utilClassListener.requiredUtilClassKind
158
147
? : return @waitForCountDown // no util class needed
159
148
160
149
val existingUtilClass = model.codegenLanguage.getUtilClassOrNull(model.project, model.testModule)
161
- if (shouldCreateOrUpdateUtilClass(existingUtilClass, mockFrameworkUsed, utilClassKind)) {
150
+ val utilClassKind = newUtilClassKindOrNull(existingUtilClass, requiredUtilClassKind)
151
+ if (utilClassKind != null ) {
162
152
createOrUpdateUtilClass(
163
153
testDirectory = baseTestDirectory,
164
154
utilClassKind = utilClassKind,
@@ -196,40 +186,50 @@ object CodeGenerationController {
196
186
}
197
187
}
198
188
199
- private fun shouldCreateOrUpdateUtilClass (
200
- existingUtilClass : PsiFile ? ,
201
- mockFrameworkUsed : Boolean ,
202
- requiredUtilClassKind : UtilClassKind
203
- ): Boolean {
204
- val mockFrameworkNotUsed = ! mockFrameworkUsed
205
-
206
- val utilClassExists = existingUtilClass != null
207
-
208
- if (! utilClassExists) {
209
- // If no util class exists, then we should create a new one.
210
- return true
189
+ /* *
190
+ * This method decides whether to overwrite an existing util class with a new one. And if so, then with what kind of util class.
191
+ * - If no util class exists, then we generate a new one.
192
+ * - If existing util class' version is out of date, then we overwrite it with a new one.
193
+ * But we use the maximum of two kinds (existing and the new one) to avoid problems with mocks.
194
+ * - If existing util class is up-to-date **and** has a greater or equal priority than the new one,
195
+ * then we do not need to overwrite it (return null).
196
+ * - Lastly, if the new util class kind has a greater priority than the existing one,
197
+ * then we do overwrite it with a newer version.
198
+ *
199
+ * @param existingUtilClass a [PsiFile] representing a file of an existing util class. If it does not exist, then [existingUtilClass] is `null`.
200
+ * @param requiredUtilClassKind the kind of the new util class that we attempt to generate.
201
+ * @return an [UtilClassKind] of a new util class that will be created or `null`, if no new util class is needed.
202
+ */
203
+ private fun newUtilClassKindOrNull (existingUtilClass : PsiFile ? , requiredUtilClassKind : UtilClassKind ): UtilClassKind ? {
204
+ if (existingUtilClass == null ) {
205
+ // If no util class exists, then we should create a new one with the given kind.
206
+ return requiredUtilClassKind
211
207
}
212
208
213
- val existingUtilClassVersion = existingUtilClass? .utilClassVersionOrNull
209
+ val existingUtilClassVersion = existingUtilClass.utilClassVersionOrNull ? : return requiredUtilClassKind
214
210
val newUtilClassVersion = requiredUtilClassKind.utilClassVersion
215
211
val versionIsUpdated = existingUtilClassVersion != newUtilClassVersion
216
212
213
+ val existingUtilClassKind = existingUtilClass.utilClassKindOrNull ? : return requiredUtilClassKind
214
+
217
215
if (versionIsUpdated) {
218
- // If an existing util class is out of date,
219
- // then we must overwrite it with a newer version.
220
- return true
216
+ // If an existing util class is out of date, then we must overwrite it with a newer version.
217
+ // But we choose the kind with more priority, because it is possible that
218
+ // the existing util class needed mocks, but the new one doesn't.
219
+ // In this case we still want to support mocks, because the previously generated tests
220
+ // expect that the util class does support them.
221
+ return maxOfNullable(existingUtilClassKind, requiredUtilClassKind)
221
222
}
222
223
223
- if (mockFrameworkNotUsed) {
224
- // If util class already exists and mock framework is not used,
225
- // then existing util class is enough, and we don't need to generate a new one.
226
- // That's because both regular and mock versions of util class can work
227
- // with tests that do not use mocks, so we do not have to worry about
228
- // version of util class that we have at the moment.
229
- return false
224
+ if (requiredUtilClassKind <= existingUtilClassKind) {
225
+ // If the existing util class kind has a greater or equal priority than the new one we attempt to generate,
226
+ // then we should not do anything. The existing util class is already enough.
227
+ return null
230
228
}
231
229
232
- return true
230
+ // The last case. The existing util class has a strictly less priority than the new one.
231
+ // So we generate the new one to overwrite the previous one with it.
232
+ return requiredUtilClassKind
233
233
}
234
234
235
235
/* *
@@ -337,7 +337,7 @@ object CodeGenerationController {
337
337
}
338
338
339
339
/* *
340
- * Util class must have a comment that specifies the version of UTBot it was generated with .
340
+ * Util class must have a comment that specifies its version.
341
341
* This property represents the version specified by this comment if it exists. Otherwise, the property is `null`.
342
342
*/
343
343
private val PsiFile .utilClassVersionOrNull: String?
@@ -354,6 +354,23 @@ object CodeGenerationController {
354
354
?.trim()
355
355
}
356
356
357
+ /* *
358
+ * Util class must have a comment that specifies its kind.
359
+ * This property obtains the kind specified by this comment if it exists. Otherwise, the property is `null`.
360
+ */
361
+ private val PsiFile .utilClassKindOrNull: UtilClassKind ?
362
+ get() = runReadAction {
363
+ val utilClass = (this as ? PsiClassOwner )
364
+ ?.classes
365
+ ?.firstOrNull()
366
+ ? : return @runReadAction null
367
+
368
+ utilClass.childrenOfType<PsiComment >()
369
+ .map { comment -> comment.text }
370
+ .mapNotNull { text -> UtilClassKind .utilClassKindByCommentOrNull(text) }
371
+ .firstOrNull()
372
+ }
373
+
357
374
/* *
358
375
* @param srcClass class under test
359
376
* @return name of the package of a given [srcClass].
@@ -878,4 +895,12 @@ object CodeGenerationController {
878
895
title = " Failed to Create Class"
879
896
)
880
897
}
898
+
899
+ private fun <T : Comparable <T >> maxOfNullable (a : T ? , b : T ? ): T ? {
900
+ return when {
901
+ a == null -> b
902
+ b == null -> a
903
+ else -> maxOf(a, b)
904
+ }
905
+ }
881
906
}
0 commit comments