Skip to content

Commit 13a1f1b

Browse files
Add some tests
1 parent 13cb649 commit 13a1f1b

File tree

3 files changed

+153
-15
lines changed

3 files changed

+153
-15
lines changed

src/main/java/org/springframework/data/repository/query/Parameter.java

+9-14
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.util.Collections;
2323
import java.util.List;
2424
import java.util.Optional;
25+
import java.util.stream.Stream;
2526

2627
import org.springframework.core.MethodParameter;
2728
import org.springframework.core.ResolvableType;
@@ -32,6 +33,7 @@
3233
import org.springframework.data.repository.util.ReactiveWrapperConverters;
3334
import org.springframework.data.util.ClassTypeInformation;
3435
import org.springframework.data.util.Lazy;
36+
import org.springframework.data.util.TypeDiscoverer;
3537
import org.springframework.util.Assert;
3638

3739
/**
@@ -207,24 +209,17 @@ boolean isSort() {
207209
*/
208210
private static boolean isDynamicProjectionParameter(MethodParameter parameter) {
209211

210-
var method = parameter.getMethod();
211-
212-
if (method == null) {
213-
throw new IllegalStateException(String.format("Method parameter %s is not backed by a method!", parameter));
214-
}
215-
216-
var ownerType = ClassTypeInformation.from(parameter.getDeclaringClass());
217-
var parameterTypes = ownerType.getParameterTypes(method).get(parameter.getParameterIndex());
218-
219-
if (!parameterTypes.getType().equals(Class.class)) {
212+
if (!parameter.getParameterType().equals(Class.class)) {
220213
return false;
221214
}
222215

223-
var bound = parameterTypes.getTypeArguments().get(0);
224-
var returnType = ClassTypeInformation.fromReturnTypeOf(method);
216+
ResolvableType returnType = ResolvableType.forMethodReturnType(parameter.getMethod());
217+
if(new TypeDiscoverer(returnType).isCollectionLike() || org.springframework.util.ClassUtils.isAssignable(Stream.class, returnType.toClass())) {
218+
returnType = returnType.getGeneric(0);
219+
}
225220

226-
return bound
227-
.equals(QueryExecutionConverters.unwrapWrapperTypes(ReactiveWrapperConverters.unwrapWrapperTypes(returnType)));
221+
ResolvableType type = ResolvableType.forMethodParameter(parameter);
222+
return returnType.getType().equals(type.getGeneric(0).getType());
228223
}
229224

230225
/**

src/main/java/org/springframework/data/util/TypeDiscoverer.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -456,7 +456,7 @@ public boolean equals(Object o) {
456456

457457
TypeDiscoverer<?> that = (TypeDiscoverer<?>) o;
458458

459-
if(!ObjectUtils.nullSafeEquals(resolvableType.getType(), that.resolvableType.getType())) {
459+
if(!ObjectUtils.nullSafeEquals(getType(), that.getType())) {
460460
return false;
461461
}
462462

src/test/java/org/springframework/data/util/TypeDiscovererUnitTests.java

+143
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import static org.assertj.core.api.Assertions.*;
1919
import static org.springframework.data.util.ClassTypeInformation.from;
2020

21+
import java.lang.reflect.Field;
2122
import java.lang.reflect.Type;
2223
import java.lang.reflect.TypeVariable;
2324
import java.util.Collection;
@@ -33,6 +34,7 @@
3334
import org.mockito.Mock;
3435
import org.mockito.junit.jupiter.MockitoExtension;
3536
import org.springframework.core.ResolvableType;
37+
import org.springframework.util.ReflectionUtils;
3638

3739
/**
3840
* Unit tests for {@link TypeDiscoverer}.
@@ -180,6 +182,126 @@ void detectsSubTypes() {
180182
assertThat(type.isSubTypeOf(String.class)).isFalse();
181183
}
182184

185+
@Test
186+
void isNotEqualIfFieldsDiffer() {
187+
// should we have something like a default TypeInformation
188+
// wiht static methods for forFieldOfType(), forClass(), like the
189+
// ones we have on resolvable type and then cache the stuff there?
190+
191+
// Managed to get Stackoverflow on hashcode of Resolvable type once for caching
192+
193+
// tests for fields in same class
194+
// tests for inherited fields
195+
// tests for same signature in different classes
196+
197+
}
198+
199+
@Test
200+
// GH-2312
201+
void sameFieldNoGenericsInfoShouldBeEqual() {
202+
203+
Field addresses = ReflectionUtils.findField(Person.class, "addresses");
204+
205+
TypeDiscoverer<Object> discoverer1 = new TypeDiscoverer<>(ResolvableType.forField(addresses, Person.class));
206+
TypeDiscoverer<Object> discoverer2 = new TypeDiscoverer<>(ResolvableType.forField(addresses, Person.class));
207+
208+
assertThat(discoverer1).isEqualTo(discoverer2);
209+
assertThat(discoverer1.hashCode()).isEqualTo(discoverer2.hashCode());
210+
}
211+
212+
@Test
213+
// GH-2312
214+
void sameFieldNoGenericsWhenInherited() {
215+
216+
Field addresses = ReflectionUtils.findField(Person.class, "addresses");
217+
TypeDiscoverer<Object> discoverer1 = new TypeDiscoverer<>(ResolvableType.forField(addresses, Person.class));
218+
TypeDiscoverer<Object> discoverer2 = new TypeDiscoverer<>(ResolvableType.forField(addresses, TypeExtendingPerson.class));
219+
220+
assertThat(discoverer1).isEqualTo(discoverer2);
221+
assertThat(discoverer1.hashCode()).isEqualTo(discoverer2.hashCode());
222+
}
223+
224+
@Test
225+
// GH-2312
226+
void sameFieldNoGenericsOnDifferentTypes() {
227+
228+
Field addresses1 = ReflectionUtils.findField(Person.class, "addresses");
229+
TypeDiscoverer<Object> discoverer1 = new TypeDiscoverer<>(ResolvableType.forField(addresses1, Person.class));
230+
231+
Field addresses2 = ReflectionUtils.findField(OtherPerson.class, "addresses");
232+
TypeDiscoverer<Object> discoverer2 = new TypeDiscoverer<>(ResolvableType.forField(addresses2, OtherPerson.class));
233+
234+
assertThat(discoverer1).isEqualTo(discoverer2);
235+
assertThat(discoverer1.hashCode()).isEqualTo(discoverer2.hashCode());
236+
}
237+
238+
@Test
239+
// GH-2312
240+
void sameFieldWithGenerics() {
241+
242+
Field field1 = ReflectionUtils.findField(GenericPerson.class, "value");
243+
TypeDiscoverer<Object> discoverer1 = new TypeDiscoverer<>(ResolvableType.forField(field1, GenericPerson.class));
244+
245+
Field field2 = ReflectionUtils.findField(GenericPerson.class, "value");
246+
TypeDiscoverer<Object> discoverer2 = new TypeDiscoverer<>(ResolvableType.forField(field2, GenericPerson.class));
247+
248+
assertThat(discoverer1).isEqualTo(discoverer2);
249+
assertThat(discoverer1.hashCode()).isEqualTo(discoverer2.hashCode());
250+
}
251+
252+
@Test
253+
// GH-2312
254+
void sameFieldWithGenericsSet() {
255+
256+
Field field1 = ReflectionUtils.findField(GenericPerson.class, "value");
257+
TypeDiscoverer<Object> discoverer1 = new TypeDiscoverer<>(ResolvableType.forField(field1, TypeExtendingGenericPersonWithObject.class));
258+
259+
Field field2 = ReflectionUtils.findField(GenericPerson.class, "value");
260+
TypeDiscoverer<Object> discoverer2 = new TypeDiscoverer<>(ResolvableType.forField(field2, TypeExtendingGenericPersonWithObject.class));
261+
262+
assertThat(discoverer1).isEqualTo(discoverer2);
263+
assertThat(discoverer1.hashCode()).isEqualTo(discoverer2.hashCode());
264+
}
265+
266+
@Test
267+
// GH-2312
268+
void sameFieldWithDifferentGenericsSet() {
269+
270+
Field field1 = ReflectionUtils.findField(GenericPerson.class, "value");
271+
TypeDiscoverer<Object> discoverer1 = new TypeDiscoverer<>(ResolvableType.forField(field1, TypeExtendingGenericPersonWithObject.class));
272+
273+
Field field2 = ReflectionUtils.findField(GenericPerson.class, "value");
274+
TypeDiscoverer<Object> discoverer2 = new TypeDiscoverer<>(ResolvableType.forField(field2, TypeExtendingGenericPersonWithAddress.class));
275+
276+
assertThat(discoverer1).isNotEqualTo(discoverer2);
277+
assertThat(discoverer1.hashCode()).isNotEqualTo(discoverer2.hashCode());
278+
}
279+
280+
@Test
281+
// GH-2312
282+
void sameFieldWithDifferentNoGenericsAndObjectOneSet() {
283+
284+
Field field1 = ReflectionUtils.findField(GenericPerson.class, "value");
285+
TypeDiscoverer<Object> discoverer1 = new TypeDiscoverer<>(ResolvableType.forField(field1, GenericPerson.class));
286+
287+
Field field2 = ReflectionUtils.findField(GenericPerson.class, "value");
288+
TypeDiscoverer<Object> discoverer2 = new TypeDiscoverer<>(ResolvableType.forField(field2, TypeExtendingGenericPersonWithObject.class));
289+
290+
assertThat(discoverer1).isEqualTo(discoverer2); // TODO: notEquals
291+
assertThat(discoverer1.hashCode()).isEqualTo(discoverer2.hashCode());
292+
}
293+
294+
@Test
295+
// GH-2312
296+
void genericFieldOfType() {
297+
298+
Field field = ReflectionUtils.findField(GenericPerson.class, "value");
299+
TypeDiscoverer<Object> discoverer = new TypeDiscoverer<>(ResolvableType.forField(field, TypeExtendingGenericPersonWithAddress.class));
300+
301+
assertThat(discoverer).isEqualTo(ClassTypeInformation.from(Address.class));
302+
assertThat(discoverer.hashCode()).isEqualTo(ClassTypeInformation.from(Address.class).hashCode());
303+
}
304+
183305
@Test // #2511
184306
void considerVavrMapToBeAMap() {
185307

@@ -227,6 +349,27 @@ class Person {
227349
Iterable<Address> addressIterable;
228350
}
229351

352+
class TypeExtendingPerson {
353+
354+
}
355+
356+
class OtherPerson {
357+
Addresses addresses;
358+
}
359+
360+
class GenericPerson<T> {
361+
T value;
362+
}
363+
364+
class TypeExtendingGenericPersonWithObject extends GenericPerson<Object> {
365+
366+
}
367+
368+
class TypeExtendingGenericPersonWithAddress extends GenericPerson<Address> {
369+
370+
}
371+
372+
230373
abstract class Addresses implements Iterable<Address> {
231374

232375
}

0 commit comments

Comments
 (0)