Skip to content

Commit 59a3b62

Browse files
committed
Adapt tests to update version of org.reflections:reflections dependency
1 parent 857adfb commit 59a3b62

File tree

2 files changed

+84
-62
lines changed

2 files changed

+84
-62
lines changed

src/test/kotlin/com/ecwid/apiclient/v3/DtoContractUnitTest.kt

Lines changed: 75 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,8 @@ import org.junit.jupiter.api.Order
1818
import org.junit.jupiter.api.Test
1919
import org.junit.jupiter.api.TestMethodOrder
2020
import org.reflections.Reflections
21-
import org.reflections.scanners.MethodParameterScanner
22-
import org.reflections.scanners.SubTypesScanner
23-
import org.reflections.util.ClasspathHelper
21+
import org.reflections.scanners.Scanners
22+
import org.reflections.scanners.Scanners.SubTypes
2423
import org.reflections.util.ConfigurationBuilder
2524
import org.reflections.util.FilterBuilder
2625
import uk.co.jemos.podam.api.PodamFactoryImpl
@@ -36,21 +35,53 @@ import kotlin.reflect.jvm.javaGetter
3635
@TestMethodOrder(OrderAnnotation::class)
3736
class DtoContractUnitTest {
3837

39-
private val convertersReflections =
38+
private val convertersReflections = lazy {
4039
Reflections(
4140
ConfigurationBuilder()
42-
.filterInputsBy(FilterBuilder().includePackage("com.ecwid.apiclient.v3.converter"))
43-
.setUrls(ClasspathHelper.forPackage(""))
44-
.setScanners(MethodParameterScanner())
41+
.forPackage("com.ecwid.apiclient.v3.converter")
42+
.filterInputsBy(
43+
FilterBuilder()
44+
.includePattern("com\\.ecwid\\.apiclient\\.v3\\.converter\\..*")
45+
)
46+
.setScanners(Scanners.MethodsReturn)
47+
)
48+
}
49+
50+
private val apiRequestClassesReflections = lazy {
51+
Reflections(
52+
ConfigurationBuilder()
53+
.forPackage(ApiRequest::class.java.packageName)
54+
.filterInputsBy(
55+
FilterBuilder()
56+
.includePattern(ApiRequest::class.java.packageName + "\\..*")
57+
)
58+
.setScanners(SubTypes.filterResultsBy { true })
4559
)
60+
}
61+
62+
private val getDtoClassesToCheck = lazy {
63+
apiRequestClassesReflections
64+
.value
65+
.getSubTypesOf(Object::class.java)
66+
.filterNot { clazz -> clazz.isInterface || clazz.isAnonymousClass }
67+
.filterNot { clazz ->
68+
try {
69+
clazz.kotlin.isCompanion
70+
} catch (ignore: UnsupportedOperationException) {
71+
// Filtering file facades classes (*Kt classes) and synthetic classes (i.e. when-mappings classes)
72+
true
73+
}
74+
}
75+
.sortedBy { clazz -> clazz.canonicalName }
76+
}
4677

4778
@Test
4879
@Order(0)
4980
fun `test all DTOs marked as data classes`() {
50-
val dtoClasses = getDtoClassesToCheck()
51-
assertFalse(dtoClasses.isEmpty())
81+
val dtoClassesToCheck = getDtoClassesToCheck.value
82+
assertFalse(dtoClassesToCheck.isEmpty())
5283

53-
val problemDtoClasses = dtoClasses.filter { dtoClass ->
84+
val problemDtoClasses = dtoClassesToCheck.filter { dtoClass ->
5485
!dtoClass.kotlin.isData && isDtoShouldBeMarkedAsDataClass(dtoClass)
5586
}
5687
assertTrue(problemDtoClasses.isEmpty()) {
@@ -61,11 +92,11 @@ class DtoContractUnitTest {
6192
@Test
6293
@Order(1)
6394
fun `test all data classes DTOs has default constructor`() {
64-
val dtoDataClasses = getDtoClassesToCheck()
95+
val dtoClassesToCheck = getDtoClassesToCheck.value
6596
.filter { dtoClass -> dtoClass.kotlin.isData }
66-
assertFalse(dtoDataClasses.isEmpty())
97+
assertFalse(dtoClassesToCheck.isEmpty())
6798

68-
val problemDtoClasses = dtoDataClasses.filter { dtoDataClass ->
99+
val problemDtoClasses = dtoClassesToCheck.filter { dtoDataClass ->
69100
val constructors = dtoDataClass.constructors
70101
val hasZeroArgConstructor = constructors.any { constructor -> constructor.parameters.isEmpty() }
71102
!hasZeroArgConstructor && isDtoShouldHaveZeroArgConstructor(constructors)
@@ -80,11 +111,11 @@ class DtoContractUnitTest {
80111
@Test
81112
@Order(2)
82113
fun `test all data classes DTOs has only val parameters in their primary constructors`() {
83-
val dtoDataClasses = getDtoClassesToCheck()
114+
val dtoClassesToCheck = getDtoClassesToCheck.value
84115
.filter { dtoClass -> dtoClass.kotlin.isData }
85-
assertFalse(dtoDataClasses.isEmpty())
116+
assertFalse(dtoClassesToCheck.isEmpty())
86117

87-
val problemDtoClasses = dtoDataClasses.filter { dtoDataClass ->
118+
val problemDtoClasses = dtoClassesToCheck.filter { dtoDataClass ->
88119
isPrimaryConstructorHasMutableProperties(dtoDataClass)
89120
}
90121
assertTrue(problemDtoClasses.isEmpty()) {
@@ -104,12 +135,12 @@ class DtoContractUnitTest {
104135
ApiResultDTO::class.java
105136
)
106137

107-
val dtoDataClasses = getDtoClassesToCheck()
138+
val dtoClassesToCheck = getDtoClassesToCheck.value
108139
.filterNot { dtoClass -> dtoClass.isEnum }
109140
.filterNot { dtoClass -> dtoClass.packageName.startsWith("com.ecwid.apiclient.v3.dto.common") }
110-
assertFalse(dtoDataClasses.isEmpty())
141+
assertFalse(dtoClassesToCheck.isEmpty())
111142

112-
val problemDtoClasses = dtoDataClasses
143+
val problemDtoClasses = dtoClassesToCheck
113144
.filterNot { dtoClass -> dtoClass.isClassifiedDTOOrEnclosingClass(*dtoMarkerInterfaces) }
114145
assertTrue(problemDtoClasses.isEmpty()) {
115146
val interfacesStr = dtoMarkerInterfaces.joinToString(separator = ", ") { int -> int.simpleName }
@@ -121,7 +152,7 @@ class DtoContractUnitTest {
121152
@Test
122153
@Order(4)
123154
fun `test all DTOs marked as 'preferably having non-nullable fields' have only non-nullable fields or fields added to exclusion list`() {
124-
val dtoDataClasses = getDtoClassesToCheck()
155+
val dtoClassesToCheck = getDtoClassesToCheck.value
125156
.filter { dtoClass ->
126157
dtoClass.isClassifiedDTOOrEnclosingClass(
127158
ApiFetchedDTO::class.java,
@@ -130,14 +161,14 @@ class DtoContractUnitTest {
130161
)
131162
}
132163
.filterNot { dtoClass -> dtoClass.kotlin.visibility == KVisibility.PRIVATE }
133-
assertFalse(dtoDataClasses.isEmpty())
164+
assertFalse(dtoClassesToCheck.isEmpty())
134165

135166
val allowedOrIgnoredNullableProperties = nullablePropertyRules
136167
.filter { rule -> rule is AllowNullable || rule is IgnoreNullable }
137168
.map { rule -> rule.property }
138169
.toSet()
139170

140-
val nullableProperties = dtoDataClasses
171+
val nullableProperties = dtoClassesToCheck
141172
.flatMap { dtoDataClass ->
142173
getPrimaryConstructorProperties(dtoDataClass)
143174
.filter { property -> property.returnType.isMarkedNullable }
@@ -170,20 +201,20 @@ class DtoContractUnitTest {
170201
@Test
171202
@Order(6)
172203
fun `test all DTOs marked as 'preferably having nullable fields' have only nullable fields or fields added to exclusion list`() {
173-
val dtoDataClasses = getDtoClassesToCheck()
204+
val dtoClassesToCheck = getDtoClassesToCheck.value
174205
.filter { dtoClass ->
175206
dtoClass.isClassifiedDTOOrEnclosingClass(
176207
ApiUpdatedDTO::class.java
177208
)
178209
}
179-
assertFalse(dtoDataClasses.isEmpty())
210+
assertFalse(dtoClassesToCheck.isEmpty())
180211

181212
val allowedOrIgnoredNonnullProperties = nonnullPropertyRules
182213
.filter { rule -> rule is AllowNonnull || rule is IgnoreNonnull }
183214
.map { rule -> rule.property }
184215
.toSet()
185216

186-
val nonnullProperties = dtoDataClasses
217+
val nonnullProperties = dtoClassesToCheck
187218
.flatMap { dtoDataClass ->
188219
getPrimaryConstructorProperties(dtoDataClass)
189220
.filterNot { property -> property.returnType.isMarkedNullable }
@@ -216,7 +247,9 @@ class DtoContractUnitTest {
216247
@Test
217248
@Order(8)
218249
fun `test fetched and updated DTOs correctly linked to each other`() {
219-
val dtoClassesToCheck = getDtoClassesToCheck()
250+
val dtoClassesToCheck = getDtoClassesToCheck.value
251+
assertFalse(dtoClassesToCheck.isEmpty())
252+
220253
val fetchedDTOClassesToModifyKindMap = getFetchedDTOClassesToModifyKindMap(dtoClassesToCheck)
221254
val updatedDTOClassesToModifyKindMap = getUpdatedDTOClassesToModifyKindMap(dtoClassesToCheck)
222255

@@ -226,6 +259,7 @@ class DtoContractUnitTest {
226259
ApiFetchedDTO.ModifyKind.ReadOnly -> {
227260
// No UpdatedDTO to check
228261
}
262+
229263
is ApiFetchedDTO.ModifyKind.ReadWrite -> {
230264
val updatedDTOClass = kind.updatedDTOClass
231265
val updatedDtoModifyKind = updatedDTOClassesToModifyKindMap[updatedDTOClass]
@@ -236,6 +270,7 @@ class DtoContractUnitTest {
236270
"Classes ${dtoClass.qualifiedName} and ${updatedDTOClass.qualifiedName} does not links to each other"
237271
)
238272
}
273+
239274
null -> {
240275
fail("Impossible situation")
241276
}
@@ -254,9 +289,11 @@ class DtoContractUnitTest {
254289
ApiFetchedDTO.ModifyKind.ReadOnly -> {
255290
fail("Updatable class ${dtoClass.qualifiedName} links to class ${fetchedDTOClass.qualifiedName} which is marked as read-only ")
256291
}
292+
257293
is ApiFetchedDTO.ModifyKind.ReadWrite -> {
258294
// Backlink was checked before
259295
}
296+
260297
null -> {
261298
fail("Impossible situation")
262299
}
@@ -269,7 +306,8 @@ class DtoContractUnitTest {
269306
@Test
270307
@Order(9)
271308
fun `test fetched and updated DTOs fields list and their types are synchronized`() {
272-
val dtoClassesToCheck = getDtoClassesToCheck()
309+
val dtoClassesToCheck = getDtoClassesToCheck.value
310+
assertFalse(dtoClassesToCheck.isEmpty())
273311

274312
val fetchedUpdatedDTOs = dtoClassesToCheck
275313
.filter { dtoClass ->
@@ -315,7 +353,9 @@ class DtoContractUnitTest {
315353
val dataStrategy = DTORandomDataProviderStrategy()
316354
val factory = PodamFactoryImpl(dataStrategy)
317355

318-
val dtoClassesToCheck = getDtoClassesToCheck()
356+
val dtoClassesToCheck = getDtoClassesToCheck.value
357+
assertFalse(dtoClassesToCheck.isEmpty())
358+
319359
val fetchedDTOClassesToModifyKindMap = getFetchedDTOClassesToModifyKindMap(dtoClassesToCheck)
320360

321361
val problemMessages = mutableListOf<String>()
@@ -326,13 +366,14 @@ class DtoContractUnitTest {
326366
// No UpdatedDTO to check
327367
null
328368
}
369+
329370
is ApiFetchedDTO.ModifyKind.ReadWrite -> {
330371
@Suppress("UNCHECKED_CAST")
331372
val fetchedDtoKClass = fetchedDtoClass as KClass<out ApiFetchedDTO>
332373
val updatedDTOClass = kind.updatedDTOClass
333374

334375
val toUpdatedMethod: Method? =
335-
findToUpdatedMethod(convertersReflections, fetchedDtoKClass, updatedDTOClass)
376+
findToUpdatedMethod(convertersReflections.value, fetchedDtoKClass, updatedDTOClass)
336377
if (toUpdatedMethod == null) {
337378
problemMessages += "Extension function with signature `${fetchedDtoKClass.java.canonicalName}.toUpdated(): ${updatedDTOClass.java.canonicalName}` is not implemented"
338379
null
@@ -399,9 +440,11 @@ private fun FieldProblem.buildMessage(): String {
399440
FieldProblemKind.FIELD_NOT_FOUND -> {
400441
"All updatable fields from Fetched DTO must have corresponding field in Updated DTO."
401442
}
443+
402444
FieldProblemKind.TYPE_MUST_NOT_BE_REUSED -> {
403445
"Fields with the same name in Fetched and Updated DTOs must not share the same DTO (except for primitives and enums)"
404446
}
447+
405448
FieldProblemKind.FIELD_IS_NOT_MAP,
406449
FieldProblemKind.FIELD_IS_NOT_LIST,
407450
FieldProblemKind.PRIMITIVE_FIELDS_INCOMPATIBLE_TYPE -> {
@@ -415,6 +458,7 @@ private fun FieldProblem.buildMessage(): String {
415458
"If this field is read-only in Ecwid API you CAN add it as `ReadOnly()` exclusion to file `NonUpdatablePropertyRules.kt`.\n" +
416459
"You MUST NOT add exclusion with type Ignored() which is used only for old fields until they are fixed."
417460
}
461+
418462
FieldProblemKind.FIELD_IS_NOT_MAP,
419463
FieldProblemKind.FIELD_IS_NOT_LIST,
420464
FieldProblemKind.PRIMITIVE_FIELDS_INCOMPATIBLE_TYPE,
@@ -475,10 +519,12 @@ private fun isDtoShouldBeMarkedAsDataClass(dtoClass: Class<*>): Boolean {
475519
// Sealed classes must not be instantiated by themself but their inheritors must be marked as data classes
476520
false
477521
}
522+
478523
kclass.objectInstance != null -> {
479524
// Singleton classes has no explicit constructor arguments so it cannot be marked as data class
480525
false
481526
}
527+
482528
else -> {
483529
// Classes that has only one zero-arg constructor cannot be marked as data class
484530
val constructors = dtoClass.constructors
@@ -541,19 +587,6 @@ private fun getUpdatedDTOClassesToModifyKindMap(dtoClassesToCheck: List<Class<*>
541587
}
542588
}
543589

544-
internal fun getDtoClassesToCheck() = Reflections(ApiRequest::class.java.packageName, SubTypesScanner(false))
545-
.getSubTypesOf(Object::class.java)
546-
.filterNot { clazz -> clazz.isInterface || clazz.isAnonymousClass }
547-
.filterNot { clazz ->
548-
try {
549-
clazz.kotlin.isCompanion
550-
} catch (ignore: UnsupportedOperationException) {
551-
// Filtering file facades classes (*Kt classes) and synthetic classes (i.e. when-mappings classes)
552-
true
553-
}
554-
}
555-
.sortedBy { clazz -> clazz.canonicalName }
556-
557590
private fun findToUpdatedMethod(
558591
reflections: Reflections,
559592
fetchedDtoClass: KClass<out ApiFetchedDTO>,

src/test/kotlin/com/ecwid/apiclient/v3/util/DTORandomDataProviderStrategy.kt

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
package com.ecwid.apiclient.v3.util
22

3-
import com.google.common.base.Predicate
4-
import com.google.common.base.Predicates
5-
import org.reflections.ReflectionUtils
63
import org.reflections.Reflections
4+
import org.reflections.util.ReflectionUtilsPredicates.withClassModifier
75
import uk.co.jemos.podam.api.AbstractRandomDataProviderStrategy
86
import uk.co.jemos.podam.api.DataProviderStrategy
97
import uk.co.jemos.podam.common.PodamConstants
@@ -71,13 +69,14 @@ internal class DTORandomDataProviderStrategy : AbstractRandomDataProviderStrateg
7169

7270
@Suppress("UNCHECKED_CAST")
7371
private fun fillConcreteSubclasses(abstractClass: Class<out T>) {
74-
this.subclasses.addAll(
75-
ReflectionUtils.getAll(
76-
reflections.getSubTypesOf(abstractClass as Class<T>),
77-
Predicates.not(IS_ABSTRACT),
78-
Predicates.not(IS_SUBCLASS_OF_ENUM)
79-
)
80-
)
72+
val subclasses = reflections.getSubTypesOf(abstractClass as Class<T>)
73+
.filter { clazz ->
74+
val isAbstract = withClassModifier(Modifier.ABSTRACT).test(clazz)
75+
val isInterface = withClassModifier(Modifier.INTERFACE).test(clazz)
76+
val isSubclassOfEnum = clazz?.superclass?.isEnum == true
77+
!isAbstract && !isInterface && !isSubclassOfEnum
78+
}
79+
this.subclasses.addAll(subclasses)
8180
}
8281

8382
private fun sortClassesByName(classes: MutableList<Class<out T>>) {
@@ -87,17 +86,7 @@ internal class DTORandomDataProviderStrategy : AbstractRandomDataProviderStrateg
8786
}
8887

8988
companion object {
90-
9189
private val reflections = Reflections("com.ecwid.apiclient.v3")
92-
93-
private val IS_ABSTRACT = Predicates.or(
94-
ReflectionUtils.withClassModifier(Modifier.ABSTRACT),
95-
ReflectionUtils.withClassModifier(Modifier.INTERFACE)
96-
)
97-
98-
private val IS_SUBCLASS_OF_ENUM = Predicate<Class<*>> { clazz ->
99-
clazz?.superclass?.isEnum == true
100-
}
10190
}
10291
}
10392
}

0 commit comments

Comments
 (0)