diff --git a/src/main/java/org/jeasy/random/CollectionPopulator.java b/src/main/java/org/jeasy/random/CollectionPopulator.java index 2b51f52c..c7230302 100644 --- a/src/main/java/org/jeasy/random/CollectionPopulator.java +++ b/src/main/java/org/jeasy/random/CollectionPopulator.java @@ -41,15 +41,18 @@ class CollectionPopulator { private final EasyRandom easyRandom; - CollectionPopulator(final EasyRandom easyRandom) { + private final GenericResolver genericResolver; + + CollectionPopulator(final EasyRandom easyRandom, final GenericResolver genericResolver) { this.easyRandom = easyRandom; + this.genericResolver = genericResolver; } @SuppressWarnings({"unchecked", "rawtypes"}) Collection getRandomCollection(final Field field, final RandomizationContext context) { int randomSize = getRandomCollectionSize(context.getParameters()); - Class fieldType = field.getType(); - Type fieldGenericType = field.getGenericType(); + Class fieldType = genericResolver.resolveRawFieldType(field, context); + Type fieldGenericType = genericResolver.resolveFieldType(field, context); Collection collection; if (isInterface(fieldType)) { diff --git a/src/main/java/org/jeasy/random/EasyRandom.java b/src/main/java/org/jeasy/random/EasyRandom.java index c1018a57..6c34e1d6 100644 --- a/src/main/java/org/jeasy/random/EasyRandom.java +++ b/src/main/java/org/jeasy/random/EasyRandom.java @@ -57,6 +57,8 @@ public class EasyRandom extends Random { private final ExclusionPolicy exclusionPolicy; + private final GenericResolver genericResolver; + /** * Create a new {@link EasyRandom} instance with default parameters. */ @@ -77,13 +79,14 @@ public EasyRandom(final EasyRandomParameters easyRandomParameters) { randomizerProvider.setRandomizerRegistries(registries); objectFactory = easyRandomParameters.getObjectFactory(); arrayPopulator = new ArrayPopulator(this); - CollectionPopulator collectionPopulator = new CollectionPopulator(this); - MapPopulator mapPopulator = new MapPopulator(this, objectFactory); + genericResolver = new GenericResolver(); + CollectionPopulator collectionPopulator = new CollectionPopulator(this, genericResolver); + MapPopulator mapPopulator = new MapPopulator(this, objectFactory, genericResolver); OptionalPopulator optionalPopulator = new OptionalPopulator(this); enumRandomizersByType = new ConcurrentHashMap<>(); fieldPopulator = new FieldPopulator(this, this.randomizerProvider, arrayPopulator, - collectionPopulator, mapPopulator, optionalPopulator); + collectionPopulator, mapPopulator, optionalPopulator, this.genericResolver); exclusionPolicy = easyRandomParameters.getExclusionPolicy(); parameters = easyRandomParameters; } @@ -143,6 +146,7 @@ T doPopulateBean(final Class type, final RandomizationContext context) { T result; try { + genericResolver.resolveInheritanceGenerics(type, context); Randomizer randomizer = randomizerProvider.getRandomizerByType(type, context); if (randomizer != null) { @@ -190,6 +194,8 @@ T doPopulateBean(final Class type, final RandomizationContext context) { } else { throw new ObjectCreationException("Unable to create a random instance of type " + type, e); } + } finally { + context.popGenericsContext(); } } diff --git a/src/main/java/org/jeasy/random/EasyRandomParameters.java b/src/main/java/org/jeasy/random/EasyRandomParameters.java index 4215f8a1..a5b87518 100644 --- a/src/main/java/org/jeasy/random/EasyRandomParameters.java +++ b/src/main/java/org/jeasy/random/EasyRandomParameters.java @@ -98,6 +98,7 @@ public class EasyRandomParameters { private boolean overrideDefaultInitialization; private boolean ignoreRandomizationErrors; private boolean bypassSetters; + private boolean advancedGenericParseMechanism; private Range collectionSizeRange; private Range stringLengthRange; private Range dateRange; @@ -123,6 +124,7 @@ public EasyRandomParameters() { overrideDefaultInitialization = false; ignoreRandomizationErrors = false; bypassSetters = false; + advancedGenericParseMechanism = false; objectPoolSize = DEFAULT_OBJECT_POOL_SIZE; randomizationDepth = DEFAULT_RANDOMIZATION_DEPTH; dateRange = new Range<>(DEFAULT_DATES_RANGE.getMin().toLocalDate(), DEFAULT_DATES_RANGE.getMax().toLocalDate()); @@ -231,6 +233,14 @@ public void setBypassSetters(boolean bypassSetters) { this.bypassSetters = bypassSetters; } + public boolean isAdvancedGenericParseMechanism() { + return advancedGenericParseMechanism; + } + + public void setAdvancedGenericParseMechanism(boolean advancedGenericParseMechanism) { + this.advancedGenericParseMechanism = advancedGenericParseMechanism; + } + public ExclusionPolicy getExclusionPolicy() { return exclusionPolicy; } @@ -561,6 +571,17 @@ public EasyRandomParameters bypassSetters(boolean bypassSetters) { return this; } + /** + * Flag to use advanced generic parse mechanism allowed resolve any TypeVariable, WildCards, etc. + * + * @param advancedGenericParseMechanism true if parse mechanism enabled + * @return the current {@link EasyRandomParameters} instance for method chaining + */ + public EasyRandomParameters advancedGenericParseMechanism(boolean advancedGenericParseMechanism) { + setAdvancedGenericParseMechanism(advancedGenericParseMechanism); + return this; + } + /** * Utility class to hold a range of values. * diff --git a/src/main/java/org/jeasy/random/FieldPopulator.java b/src/main/java/org/jeasy/random/FieldPopulator.java index 83cb76f3..fbd79345 100644 --- a/src/main/java/org/jeasy/random/FieldPopulator.java +++ b/src/main/java/org/jeasy/random/FieldPopulator.java @@ -35,6 +35,7 @@ import org.slf4j.LoggerFactory; import java.lang.reflect.Field; +import java.lang.reflect.GenericArrayType; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Type; @@ -68,15 +69,19 @@ class FieldPopulator { private final RandomizerProvider randomizerProvider; + private final GenericResolver genericResolver; + FieldPopulator(final EasyRandom easyRandom, final RandomizerProvider randomizerProvider, final ArrayPopulator arrayPopulator, final CollectionPopulator collectionPopulator, - final MapPopulator mapPopulator, OptionalPopulator optionalPopulator) { + final MapPopulator mapPopulator, OptionalPopulator optionalPopulator, + final GenericResolver genericResolver) { this.easyRandom = easyRandom; this.randomizerProvider = randomizerProvider; this.arrayPopulator = arrayPopulator; this.collectionPopulator = collectionPopulator; this.mapPopulator = mapPopulator; this.optionalPopulator = optionalPopulator; + this.genericResolver = genericResolver; } void populateField(final Object target, final Field field, final RandomizationContext context) throws IllegalAccessException { @@ -127,7 +132,7 @@ private Randomizer getRandomizer(Field field, RandomizationContext context) { Type genericType = field.getGenericType(); if (isTypeVariable(genericType)) { // if generic type, retrieve actual type from declaring class - Class type = getParametrizedType(field, context); + Class type = getFieldType(field, context); randomizer = randomizerProvider.getRandomizerByType(type, context); } else { randomizer = randomizerProvider.getRandomizerByType(field.getType(), context); @@ -137,8 +142,8 @@ private Randomizer getRandomizer(Field field, RandomizationContext context) { } private Object generateRandomValue(final Field field, final RandomizationContext context) { - Class fieldType = field.getType(); - Type fieldGenericType = field.getGenericType(); + Class fieldType = genericResolver.resolveRawFieldType(field, context); + Type fieldGenericType = genericResolver.resolveFieldType(field, context); if (isArrayType(fieldType)) { return arrayPopulator.getRandomArray(fieldType, context); @@ -158,10 +163,10 @@ private Object generateRandomValue(final Field field, final RandomizationContext return easyRandom.doPopulateBean(randomConcreteSubType, context); } } else { - Type genericType = field.getGenericType(); + Type genericType = genericResolver.resolveFieldType(field, context); if (isTypeVariable(genericType)) { // if generic type, try to retrieve actual type from hierarchy - Class type = getParametrizedType(field, context); + Class type = getFieldType(field, context); return easyRandom.doPopulateBean(type, context); } return easyRandom.doPopulateBean(fieldType, context); @@ -210,4 +215,12 @@ private Type getGenericSuperClass(RandomizationContext context) { } return genericSuperclass; } + + private Class getFieldType(Field field, RandomizationContext context) { + if (context.getParameters().isAdvancedGenericParseMechanism()) { + return genericResolver.resolveRawFieldType(field, context); + } else { + return getParametrizedType(field, context); + } + } } diff --git a/src/main/java/org/jeasy/random/GenericResolver.java b/src/main/java/org/jeasy/random/GenericResolver.java new file mode 100644 index 00000000..3366bf05 --- /dev/null +++ b/src/main/java/org/jeasy/random/GenericResolver.java @@ -0,0 +1,234 @@ +/* + * The MIT License + * + * Copyright (c) 2023, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.jeasy.random; + +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.lang.reflect.WildcardType; +import java.util.ArrayList; +import java.util.List; + +public class GenericResolver { + + void resolveInheritanceGenerics(Class klass, RandomizationContext context) { + context.pushGenericsContext(); + if (context.getParameters().isAdvancedGenericParseMechanism()) { + resolveCurrentGenerics(klass, context); + + Class previousClass = klass; + Type genericSuperclass = klass.getGenericSuperclass(); + while (genericSuperclass != null && !genericSuperclass.equals(Object.class)) { + if (genericSuperclass instanceof ParameterizedType parameterizedType) { + Class rawType = (Class) parameterizedType.getRawType(); + + TypeVariable[] typeParameters = rawType.getTypeParameters(); + Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); + int typeArgumentsCount = typeParameters.length; + + for (int i = 0; i < typeArgumentsCount; i++) { + TypeVariable typeVariable = typeParameters[i]; + Type actualTypeArgument = actualTypeArguments[i]; + + Type resolvedType = resolveType(actualTypeArgument, previousClass, context); + context.addTypeVariableMapping(rawType, typeVariable, resolvedType); + } + + previousClass = rawType; + genericSuperclass = rawType.getGenericSuperclass(); + } else if (genericSuperclass instanceof Class superClass) { + previousClass = superClass; + genericSuperclass = superClass.getGenericSuperclass(); + } else { + genericSuperclass = null; + } + } + } + } + + Type resolveFieldType(Field field, RandomizationContext context) { + if (context.getParameters().isAdvancedGenericParseMechanism()) { + Type genericType = field.getGenericType(); + + Type resolvedFieldType = resolveType(genericType, field.getDeclaringClass(), context); + if (resolvedFieldType instanceof ParameterizedType parameterizedType) { + Class rootClass = (Class) parameterizedType.getRawType(); + TypeVariable[] typeParameters = rootClass.getTypeParameters(); + Type[] typeArguments = parameterizedType.getActualTypeArguments(); + int argumentsCount = typeParameters.length; + + for (int i = 0; i < argumentsCount; i++) { + TypeVariable typeVariable = typeParameters[i]; + Type actualTypeArgument = typeArguments[i]; + + context.addFieldTypeVariableMapping(field, typeVariable, actualTypeArgument); + } + } + + return resolvedFieldType; + } else { + return field.getGenericType(); + } + } + + Class resolveRawFieldType(Field field, RandomizationContext context) { + if (context.getParameters().isAdvancedGenericParseMechanism()) { + Type resolvedFieldType = resolveFieldType(field, context); + if (resolvedFieldType instanceof Class resolvedClass) { + return resolvedClass; + } else if (resolvedFieldType instanceof ParameterizedType parameterizedType) { + return (Class) parameterizedType.getRawType(); + } + } + + return field.getType(); + } + + private Type resolveType(Type type, Class contextClass, RandomizationContext context) { + if (type instanceof TypeVariable actualTypeVariable) { + Type resolvedType = context.resolveTypeVariable(contextClass, actualTypeVariable); + if (resolvedType != null) { + return resolvedType; + } + } else if (type instanceof GenericArrayType genericArrayType) { + return resolveGenericArrayType(genericArrayType, contextClass, context); + } else if (type instanceof ParameterizedType internalParametrizedType) { + return resolveParametrizedType(internalParametrizedType, contextClass, context); + } else if (type instanceof WildcardType wildcardType) { + return resolveWildCardType(wildcardType, contextClass, context); + } + + return type; + } + + private void resolveCurrentGenerics(Class klass, RandomizationContext context) { + TypeVariable>[] initialTypeParameters = klass.getTypeParameters(); + for (TypeVariable> typeVariable : initialTypeParameters) { + Type realType = context.resolveFieldTypeVariable(typeVariable); + context.addTypeVariableMapping(klass, typeVariable, realType); + } + } + + private ParameterizedType resolveParametrizedType( + ParameterizedType parameterizedType, + Class contextClass, + RandomizationContext context + ) { + List resolvedTypes = new ArrayList<>(); + + for (Type actualTypeArgument : parameterizedType.getActualTypeArguments()) { + resolvedTypes.add(resolveType(actualTypeArgument, contextClass, context)); + } + + return new ParametrizedTypeImpl( + resolvedTypes.toArray(new Type[0]), + parameterizedType.getRawType(), + parameterizedType.getOwnerType() + ); + } + + private Type resolveWildCardType(WildcardType wildcardType, Class contextClass, RandomizationContext context) { + Type[] upperBounds = wildcardType.getUpperBounds(); + if (upperBounds.length == 1) { + Type upperBound = upperBounds[0]; + if (!upperBound.equals(Object.class)) { + return resolveType(upperBound, contextClass, context); + } + } + + Type[] lowerBounds = wildcardType.getLowerBounds(); + if (lowerBounds.length == 1) { + Type lowerBound = lowerBounds[0]; + if (!lowerBound.equals(Object.class)) { + return resolveType(lowerBound, contextClass, context); + } + } + + return wildcardType; + } + + private Type resolveGenericArrayType( + GenericArrayType genericArrayType, + Class contextClass, + RandomizationContext context + ) { + Type arrayComponentType = genericArrayType.getGenericComponentType(); + Type resolvedType = resolveType(arrayComponentType, contextClass, context); + + if (resolvedType instanceof Class realClass) { + return Array.newInstance(realClass, 0).getClass(); + } else if (resolvedType instanceof ParameterizedType || resolvedType instanceof GenericArrayType) { + return new GenericArrayTypeImpl(resolvedType); + } else { + return genericArrayType; + } + } + + private static class ParametrizedTypeImpl implements ParameterizedType { + + private final Type[] actualTypeArguments; + + private final Type rawType; + + private final Type ownerType; + + public ParametrizedTypeImpl(Type[] actualTypeArguments, Type rawType, Type ownerType) { + this.actualTypeArguments = actualTypeArguments; + this.rawType = rawType; + this.ownerType = ownerType; + } + + @Override + public Type[] getActualTypeArguments() { + return actualTypeArguments; + } + + @Override + public Type getRawType() { + return rawType; + } + + @Override + public Type getOwnerType() { + return ownerType; + } + } + + private static class GenericArrayTypeImpl implements GenericArrayType { + + private final Type componentType; + + private GenericArrayTypeImpl(Type componentType) { + this.componentType = componentType; + } + + @Override + public Type getGenericComponentType() { + return componentType; + } + } +} diff --git a/src/main/java/org/jeasy/random/MapPopulator.java b/src/main/java/org/jeasy/random/MapPopulator.java index cc40e7e1..76415acf 100644 --- a/src/main/java/org/jeasy/random/MapPopulator.java +++ b/src/main/java/org/jeasy/random/MapPopulator.java @@ -46,16 +46,19 @@ class MapPopulator { private final ObjectFactory objectFactory; - MapPopulator(final EasyRandom easyRandom, final ObjectFactory objectFactory) { + private final GenericResolver genericResolver; + + MapPopulator(final EasyRandom easyRandom, final ObjectFactory objectFactory, final GenericResolver genericResolver) { this.easyRandom = easyRandom; this.objectFactory = objectFactory; + this.genericResolver = genericResolver; } @SuppressWarnings("unchecked") Map getRandomMap(final Field field, final RandomizationContext context) { int randomSize = getRandomMapSize(context.getParameters()); - Class fieldType = field.getType(); - Type fieldGenericType = field.getGenericType(); + Class fieldType = genericResolver.resolveRawFieldType(field, context); + Type fieldGenericType = genericResolver.resolveFieldType(field, context); Map map; if (isInterface(fieldType)) { diff --git a/src/main/java/org/jeasy/random/RandomizationContext.java b/src/main/java/org/jeasy/random/RandomizationContext.java index c4041717..54a8bc1d 100644 --- a/src/main/java/org/jeasy/random/RandomizationContext.java +++ b/src/main/java/org/jeasy/random/RandomizationContext.java @@ -26,6 +26,8 @@ import org.jeasy.random.api.RandomizerContext; import java.lang.reflect.Field; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; import java.util.*; import static java.util.stream.Collectors.toList; @@ -44,6 +46,10 @@ class RandomizationContext implements RandomizerContext { private final Stack stack; + private final Stack, Map>> classTypeVariableMapStack; + + private final Map> fieldsTypeVariableMap; + private final Class type; private final Random random; @@ -56,6 +62,8 @@ class RandomizationContext implements RandomizerContext { stack = new Stack<>(); this.parameters = parameters; this.random = new Random(parameters.getSeed()); + this.classTypeVariableMapStack = new Stack<>(); + this.fieldsTypeVariableMap = new HashMap<>(); } void addPopulatedBean(final Class type, Object object) { @@ -88,6 +96,42 @@ void popStackItem() { stack.pop(); } + void pushGenericsContext() { + classTypeVariableMapStack.push(new HashMap<>()); + } + + void popGenericsContext() { + classTypeVariableMapStack.pop(); + } + + void addTypeVariableMapping(final Class carrierClass, final TypeVariable typeVariable, final Type realType) { + Map, Map> genericClassContext = classTypeVariableMapStack.peek(); + Map typeVariableMap = genericClassContext.computeIfAbsent(carrierClass, key -> new HashMap<>()); + typeVariableMap.put(typeVariable.getName(), realType); + } + + void addFieldTypeVariableMapping(final Field field, final TypeVariable typeVariable, final Type realType) { + Map typeVariableMap = fieldsTypeVariableMap.computeIfAbsent(field, key -> new HashMap<>()); + typeVariableMap.put(typeVariable.getName(), realType); + } + + Type resolveFieldTypeVariable(final TypeVariable typeVariable) { + Field field = stack.peek().getField(); + Map typeVariableMap = fieldsTypeVariableMap.computeIfAbsent(field, key -> new HashMap<>()); + + return typeVariableMap.get(typeVariable.getName()); + } + + Type resolveTypeVariable(final Class genericCarrierClass, final TypeVariable typeVariable) { + Map, Map> typeVariablesMap = classTypeVariableMapStack.peek(); + Map genericsMappingByCarrierClass = typeVariablesMap.getOrDefault( + genericCarrierClass, + Collections.emptyMap() + ); + + return genericsMappingByCarrierClass.get(typeVariable.getName()); + } + String getFieldFullName(final Field field) { List pathToField = getStackedFieldNames(); pathToField.add(field.getName()); diff --git a/src/test/java/org/jeasy/random/AdvancedGenericsTest.java b/src/test/java/org/jeasy/random/AdvancedGenericsTest.java new file mode 100644 index 00000000..48684ec0 --- /dev/null +++ b/src/test/java/org/jeasy/random/AdvancedGenericsTest.java @@ -0,0 +1,180 @@ +/* + * The MIT License + * + * Copyright (c) 2023, Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.jeasy.random; + +import org.jeasy.random.beans.TestEnum; +import org.jeasy.random.beans.generics.ClassWithGenericArray; +import org.jeasy.random.beans.generics.ClassWithCommonGeneric; +import org.jeasy.random.beans.generics.ClassWithArrayImpl; +import org.jeasy.random.beans.generics.ClassWithArrayInGeneric; +import org.jeasy.random.beans.generics.ClassWithList; +import org.jeasy.random.beans.generics.ClassDifficultImpl; +import org.jeasy.random.beans.generics.ClassStringImpl; +import org.jeasy.random.beans.generics.ClassWithListInGenericImpl; +import org.jeasy.random.beans.generics.ClassUnusedGenericInInheritance; +import org.jeasy.random.beans.generics.ClassWithTwoDimArrayImpl; +import org.jeasy.random.beans.generics.ClassWithGenericFieldImpl; +import org.jeasy.random.beans.generics.ClassWithReThrownGeneric; +import org.jeasy.random.beans.generics.ClassUnusedGenericField; +import org.jeasy.random.beans.generics.ClassWithTwoLevelGeneric; +import org.jeasy.random.beans.generics.ClassWithFieldTwoLevelGeneric; +import org.jeasy.random.beans.generics.ClassWithWildCards; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class AdvancedGenericsTest { + + private final EasyRandom easyRandom = new EasyRandom(new EasyRandomParameters() + .advancedGenericParseMechanism(true)); + + @Test + void commonGenericTest() { + ClassStringImpl object = easyRandom.nextObject(ClassStringImpl.class); + String genericField = object.getGenericField(); + + assertThat(genericField).isNotEmpty(); + } + + @Test + void commonListGenericTest() { + ClassWithListInGenericImpl object = easyRandom.nextObject(ClassWithListInGenericImpl.class); + List genericField = object.getGenericField(); + + assertThat(genericField).isNotEmpty(); + } + + @Test + void commonArrayTest() { + ClassWithArrayInGeneric object = easyRandom.nextObject(ClassWithArrayInGeneric.class); + String[] genericField = object.getGenericField(); + + assertThat(genericField).isNotEmpty(); + } + + @Test + void nonDeterminateGenericTest() { + assertThrows(ObjectCreationException.class, () -> { + easyRandom.nextObject(ClassWithReThrownGeneric.class); + }); + } + + @Test + void twoLevelGenericTest() { + ClassWithTwoLevelGeneric object = easyRandom.nextObject(ClassWithTwoLevelGeneric.class); + Integer genericField = object.getGenericField(); + + assertThat(genericField).isNotNull(); + } + + @Test + void determinateArrayCommonGenericTest() { + ClassWithArrayImpl object = easyRandom.nextObject(ClassWithArrayImpl.class); + String[] genericField = object.getGenericArray(); + + assertThat(genericField).isNotEmpty(); + } + + @Test + void determinateTwoLevelArrayCommonGenericTest() { + ClassWithTwoDimArrayImpl object = easyRandom.nextObject(ClassWithTwoDimArrayImpl.class); + Integer[][] genericField = object.getGenericArray(); + + assertThat(genericField).isNotEmpty(); + } + + @Test + void determinateCollectionCommonGenericTest() { + ClassWithList object = easyRandom.nextObject(ClassWithList.class); + List genericList = object.getGenericList(); + + assertThat(genericList).isNotEmpty(); + } + + @Test + void genericFieldTest() { + ClassWithGenericFieldImpl object = easyRandom.nextObject(ClassWithGenericFieldImpl.class); + ClassWithGenericArray arrayCommonGeneric = object.getGenericField(); + + String[] genericArray = arrayCommonGeneric.getGenericArray(); + + assertThat(genericArray).isNotEmpty(); + } + + @Test + void twoLevelGenericFieldTest() { + ClassWithFieldTwoLevelGeneric object = easyRandom.nextObject(ClassWithFieldTwoLevelGeneric.class); + ClassWithReThrownGeneric genericField = object.getGenericField(); + Integer integerGenericField = genericField.getGenericField(); + + assertThat(integerGenericField).isNotNull(); + } + + @Test + void difficultGenericTest() { + ClassDifficultImpl object = easyRandom.nextObject(ClassDifficultImpl.class); + List> genericList = object.getGenericList(); + ClassWithReThrownGeneric genericField = object.getGenericField(); + + for (ClassWithReThrownGeneric integerNonDeterminateGeneric : genericList) { + Integer integerGeneric = integerNonDeterminateGeneric.getGenericField(); + assertThat(integerGeneric).isNotNull(); + } + + Double doubleGeneric = genericField.getGenericField(); + assertThat(doubleGeneric).isNotNull(); + } + + @Test + void wildCardBaseTest() { + ClassWithWildCards object = easyRandom.nextObject(ClassWithWildCards.class); + List wildCardLowerList = object.getWildCardLowerList(); + List wildCardUpperList = object.getWildCardUpperList(); + List wildCardeEumList = object.getWildCardeEumList(); + + assertThat(wildCardLowerList).isNotEmpty(); + assertThat(wildCardUpperList).isNotEmpty(); + assertThat(wildCardeEumList).isNotEmpty(); + } + + @Test + void determinateNonGenericTest() { + ClassUnusedGenericInInheritance object = easyRandom.nextObject(ClassUnusedGenericInInheritance.class); + Object genericField = object.getGenericField(); + + assertThat(genericField).isNotNull(); + } + + @Test + void nonDeterminateGenericFieldTest() { + ClassUnusedGenericField object = easyRandom.nextObject(ClassUnusedGenericField.class); + ClassWithCommonGeneric commonGeneric = object.getCommonGeneric(); + Object genericField = commonGeneric.getGenericField(); + + assertThat(genericField).isNotNull(); + } +} diff --git a/src/test/java/org/jeasy/random/CollectionPopulatorTest.java b/src/test/java/org/jeasy/random/CollectionPopulatorTest.java index f6dd3224..9c85cb8a 100644 --- a/src/test/java/org/jeasy/random/CollectionPopulatorTest.java +++ b/src/test/java/org/jeasy/random/CollectionPopulatorTest.java @@ -67,7 +67,8 @@ class CollectionPopulatorTest { @BeforeEach void setUp() { parameters = new EasyRandomParameters().collectionSizeRange(SIZE, SIZE); - collectionPopulator = new CollectionPopulator(easyRandom); + GenericResolver genericResolver = new GenericResolver(); + collectionPopulator = new CollectionPopulator(easyRandom, genericResolver); } /* diff --git a/src/test/java/org/jeasy/random/FieldPopulatorTest.java b/src/test/java/org/jeasy/random/FieldPopulatorTest.java index eb7e03ca..3834a72b 100644 --- a/src/test/java/org/jeasy/random/FieldPopulatorTest.java +++ b/src/test/java/org/jeasy/random/FieldPopulatorTest.java @@ -72,11 +72,14 @@ class FieldPopulatorTest { @Mock private OptionalPopulator optionalPopulator; + @Mock + private GenericResolver genericResolver; + private FieldPopulator fieldPopulator; @BeforeEach void setUp() { - fieldPopulator = new FieldPopulator(easyRandom, randomizerProvider, arrayPopulator, collectionPopulator, mapPopulator, optionalPopulator); + fieldPopulator = new FieldPopulator(easyRandom, randomizerProvider, arrayPopulator, collectionPopulator, mapPopulator, optionalPopulator, genericResolver); } @Test diff --git a/src/test/java/org/jeasy/random/MapPopulatorTest.java b/src/test/java/org/jeasy/random/MapPopulatorTest.java index d5b92447..e8b67e27 100644 --- a/src/test/java/org/jeasy/random/MapPopulatorTest.java +++ b/src/test/java/org/jeasy/random/MapPopulatorTest.java @@ -66,7 +66,8 @@ class MapPopulatorTest { void setUp() { parameters = new EasyRandomParameters().collectionSizeRange(SIZE, SIZE); ObjectFactory objectFactory = new ObjenesisObjectFactory(); - mapPopulator = new MapPopulator(easyRandom, objectFactory); + GenericResolver genericResolver = new GenericResolver(); + mapPopulator = new MapPopulator(easyRandom, objectFactory, genericResolver); } /* diff --git a/src/test/java/org/jeasy/random/beans/generics/ClassDifficultImpl.java b/src/test/java/org/jeasy/random/beans/generics/ClassDifficultImpl.java new file mode 100644 index 00000000..36a0f164 --- /dev/null +++ b/src/test/java/org/jeasy/random/beans/generics/ClassDifficultImpl.java @@ -0,0 +1,4 @@ +package org.jeasy.random.beans.generics; + +public class ClassDifficultImpl extends ClassWithDifficultGeneric { +} diff --git a/src/test/java/org/jeasy/random/beans/generics/ClassStringImpl.java b/src/test/java/org/jeasy/random/beans/generics/ClassStringImpl.java new file mode 100644 index 00000000..dfb71b4c --- /dev/null +++ b/src/test/java/org/jeasy/random/beans/generics/ClassStringImpl.java @@ -0,0 +1,4 @@ +package org.jeasy.random.beans.generics; + +public class ClassStringImpl extends ClassWithCommonGeneric { +} diff --git a/src/test/java/org/jeasy/random/beans/generics/ClassUnusedGenericField.java b/src/test/java/org/jeasy/random/beans/generics/ClassUnusedGenericField.java new file mode 100644 index 00000000..476b6d73 --- /dev/null +++ b/src/test/java/org/jeasy/random/beans/generics/ClassUnusedGenericField.java @@ -0,0 +1,14 @@ +package org.jeasy.random.beans.generics; + +public class ClassUnusedGenericField { + + private ClassWithCommonGeneric commonGeneric; + + public ClassWithCommonGeneric getCommonGeneric() { + return commonGeneric; + } + + public void setCommonGeneric(ClassWithCommonGeneric commonGeneric) { + this.commonGeneric = commonGeneric; + } +} diff --git a/src/test/java/org/jeasy/random/beans/generics/ClassUnusedGenericInInheritance.java b/src/test/java/org/jeasy/random/beans/generics/ClassUnusedGenericInInheritance.java new file mode 100644 index 00000000..9623556e --- /dev/null +++ b/src/test/java/org/jeasy/random/beans/generics/ClassUnusedGenericInInheritance.java @@ -0,0 +1,4 @@ +package org.jeasy.random.beans.generics; + +public class ClassUnusedGenericInInheritance extends ClassWithCommonGeneric { +} diff --git a/src/test/java/org/jeasy/random/beans/generics/ClassWithArrayImpl.java b/src/test/java/org/jeasy/random/beans/generics/ClassWithArrayImpl.java new file mode 100644 index 00000000..7c00f6fa --- /dev/null +++ b/src/test/java/org/jeasy/random/beans/generics/ClassWithArrayImpl.java @@ -0,0 +1,4 @@ +package org.jeasy.random.beans.generics; + +public class ClassWithArrayImpl extends ClassWithGenericArray { +} diff --git a/src/test/java/org/jeasy/random/beans/generics/ClassWithArrayInGeneric.java b/src/test/java/org/jeasy/random/beans/generics/ClassWithArrayInGeneric.java new file mode 100644 index 00000000..03a96c4c --- /dev/null +++ b/src/test/java/org/jeasy/random/beans/generics/ClassWithArrayInGeneric.java @@ -0,0 +1,4 @@ +package org.jeasy.random.beans.generics; + +public class ClassWithArrayInGeneric extends ClassWithCommonGeneric { +} diff --git a/src/test/java/org/jeasy/random/beans/generics/ClassWithCommonGeneric.java b/src/test/java/org/jeasy/random/beans/generics/ClassWithCommonGeneric.java new file mode 100644 index 00000000..52df48e7 --- /dev/null +++ b/src/test/java/org/jeasy/random/beans/generics/ClassWithCommonGeneric.java @@ -0,0 +1,14 @@ +package org.jeasy.random.beans.generics; + +public class ClassWithCommonGeneric { + + private T genericField; + + public T getGenericField() { + return genericField; + } + + public void setGenericField(T genericField) { + this.genericField = genericField; + } +} diff --git a/src/test/java/org/jeasy/random/beans/generics/ClassWithDifficultGeneric.java b/src/test/java/org/jeasy/random/beans/generics/ClassWithDifficultGeneric.java new file mode 100644 index 00000000..fe126a68 --- /dev/null +++ b/src/test/java/org/jeasy/random/beans/generics/ClassWithDifficultGeneric.java @@ -0,0 +1,16 @@ +package org.jeasy.random.beans.generics; + +import java.util.List; + +public class ClassWithDifficultGeneric extends ClassWithCommonGeneric> { + + private List> genericList; + + public List> getGenericList() { + return genericList; + } + + public void setGenericList(List> genericList) { + this.genericList = genericList; + } +} diff --git a/src/test/java/org/jeasy/random/beans/generics/ClassWithFieldTwoLevelGeneric.java b/src/test/java/org/jeasy/random/beans/generics/ClassWithFieldTwoLevelGeneric.java new file mode 100644 index 00000000..1c9e968f --- /dev/null +++ b/src/test/java/org/jeasy/random/beans/generics/ClassWithFieldTwoLevelGeneric.java @@ -0,0 +1,14 @@ +package org.jeasy.random.beans.generics; + +public class ClassWithFieldTwoLevelGeneric { + + private ClassWithReThrownGeneric genericField; + + public ClassWithReThrownGeneric getGenericField() { + return genericField; + } + + public void setGenericField(ClassWithReThrownGeneric genericField) { + this.genericField = genericField; + } +} diff --git a/src/test/java/org/jeasy/random/beans/generics/ClassWithGenericArray.java b/src/test/java/org/jeasy/random/beans/generics/ClassWithGenericArray.java new file mode 100644 index 00000000..5b0a0313 --- /dev/null +++ b/src/test/java/org/jeasy/random/beans/generics/ClassWithGenericArray.java @@ -0,0 +1,14 @@ +package org.jeasy.random.beans.generics; + +public class ClassWithGenericArray { + + private T[] genericArray; + + public T[] getGenericArray() { + return genericArray; + } + + public void setGenericArray(T[] genericArray) { + this.genericArray = genericArray; + } +} diff --git a/src/test/java/org/jeasy/random/beans/generics/ClassWithGenericFieldImpl.java b/src/test/java/org/jeasy/random/beans/generics/ClassWithGenericFieldImpl.java new file mode 100644 index 00000000..1587a972 --- /dev/null +++ b/src/test/java/org/jeasy/random/beans/generics/ClassWithGenericFieldImpl.java @@ -0,0 +1,14 @@ +package org.jeasy.random.beans.generics; + +public class ClassWithGenericFieldImpl { + + private ClassWithGenericArray genericField; + + public ClassWithGenericArray getGenericField() { + return genericField; + } + + public void setGenericField(ClassWithGenericArray genericField) { + this.genericField = genericField; + } +} diff --git a/src/test/java/org/jeasy/random/beans/generics/ClassWithGenericList.java b/src/test/java/org/jeasy/random/beans/generics/ClassWithGenericList.java new file mode 100644 index 00000000..47489d40 --- /dev/null +++ b/src/test/java/org/jeasy/random/beans/generics/ClassWithGenericList.java @@ -0,0 +1,16 @@ +package org.jeasy.random.beans.generics; + +import java.util.List; + +public class ClassWithGenericList { + + private List genericList; + + public List getGenericList() { + return genericList; + } + + public void setGenericList(List genericList) { + this.genericList = genericList; + } +} diff --git a/src/test/java/org/jeasy/random/beans/generics/ClassWithList.java b/src/test/java/org/jeasy/random/beans/generics/ClassWithList.java new file mode 100644 index 00000000..a94256cf --- /dev/null +++ b/src/test/java/org/jeasy/random/beans/generics/ClassWithList.java @@ -0,0 +1,4 @@ +package org.jeasy.random.beans.generics; + +public class ClassWithList extends ClassWithGenericList { +} diff --git a/src/test/java/org/jeasy/random/beans/generics/ClassWithListInGenericImpl.java b/src/test/java/org/jeasy/random/beans/generics/ClassWithListInGenericImpl.java new file mode 100644 index 00000000..674d8abb --- /dev/null +++ b/src/test/java/org/jeasy/random/beans/generics/ClassWithListInGenericImpl.java @@ -0,0 +1,6 @@ +package org.jeasy.random.beans.generics; + +import java.util.List; + +public class ClassWithListInGenericImpl extends ClassWithCommonGeneric> { +} diff --git a/src/test/java/org/jeasy/random/beans/generics/ClassWithReThrownGeneric.java b/src/test/java/org/jeasy/random/beans/generics/ClassWithReThrownGeneric.java new file mode 100644 index 00000000..7eca3edd --- /dev/null +++ b/src/test/java/org/jeasy/random/beans/generics/ClassWithReThrownGeneric.java @@ -0,0 +1,4 @@ +package org.jeasy.random.beans.generics; + +public class ClassWithReThrownGeneric extends ClassWithCommonGeneric { +} diff --git a/src/test/java/org/jeasy/random/beans/generics/ClassWithTwoDimArray.java b/src/test/java/org/jeasy/random/beans/generics/ClassWithTwoDimArray.java new file mode 100644 index 00000000..89e97f2d --- /dev/null +++ b/src/test/java/org/jeasy/random/beans/generics/ClassWithTwoDimArray.java @@ -0,0 +1,4 @@ +package org.jeasy.random.beans.generics; + +public class ClassWithTwoDimArray extends ClassWithGenericArray { +} diff --git a/src/test/java/org/jeasy/random/beans/generics/ClassWithTwoDimArrayImpl.java b/src/test/java/org/jeasy/random/beans/generics/ClassWithTwoDimArrayImpl.java new file mode 100644 index 00000000..242d263b --- /dev/null +++ b/src/test/java/org/jeasy/random/beans/generics/ClassWithTwoDimArrayImpl.java @@ -0,0 +1,4 @@ +package org.jeasy.random.beans.generics; + +public class ClassWithTwoDimArrayImpl extends ClassWithTwoDimArray { +} diff --git a/src/test/java/org/jeasy/random/beans/generics/ClassWithTwoLevelGeneric.java b/src/test/java/org/jeasy/random/beans/generics/ClassWithTwoLevelGeneric.java new file mode 100644 index 00000000..95ed0f1f --- /dev/null +++ b/src/test/java/org/jeasy/random/beans/generics/ClassWithTwoLevelGeneric.java @@ -0,0 +1,4 @@ +package org.jeasy.random.beans.generics; + +public class ClassWithTwoLevelGeneric extends ClassWithReThrownGeneric { +} diff --git a/src/test/java/org/jeasy/random/beans/generics/ClassWithWildCards.java b/src/test/java/org/jeasy/random/beans/generics/ClassWithWildCards.java new file mode 100644 index 00000000..dcb8acf6 --- /dev/null +++ b/src/test/java/org/jeasy/random/beans/generics/ClassWithWildCards.java @@ -0,0 +1,38 @@ +package org.jeasy.random.beans.generics; + +import org.jeasy.random.beans.TestEnum; + +import java.util.List; + +public class ClassWithWildCards { + + private List wildCardUpperList; + + private List wildCardLowerList; + + private List wildCardeEumList; + + public List getWildCardUpperList() { + return wildCardUpperList; + } + + public void setWildCardUpperList(List wildCardUpperList) { + this.wildCardUpperList = wildCardUpperList; + } + + public List getWildCardLowerList() { + return wildCardLowerList; + } + + public void setWildCardLowerList(List wildCardLowerList) { + this.wildCardLowerList = wildCardLowerList; + } + + public List getWildCardeEumList() { + return wildCardeEumList; + } + + public void setWildCardeEumList(List wildCardeEumList) { + this.wildCardeEumList = wildCardeEumList; + } +}