diff --git a/docs/content/v1.1.x-kor/release-notes/_index.md b/docs/content/v1.1.x-kor/release-notes/_index.md index 7bfe7c10b..e45791342 100644 --- a/docs/content/v1.1.x-kor/release-notes/_index.md +++ b/docs/content/v1.1.x-kor/release-notes/_index.md @@ -9,6 +9,8 @@ sectionStart ## v.1.1.6 Deprecate `Randoms.create(String)`, use `Randoms.setSeed(long)` instead. +Fix an instance generated by `ConstructorPropertiesArbitraryIntrospector` that was not validated by validation annotations. + sectionEnd sectionStart diff --git a/docs/content/v1.1.x/release-notes/_index.md b/docs/content/v1.1.x/release-notes/_index.md index 541e01bf9..d5c542c2c 100644 --- a/docs/content/v1.1.x/release-notes/_index.md +++ b/docs/content/v1.1.x/release-notes/_index.md @@ -7,7 +7,9 @@ weight: 100 --- sectionStart ## v.1.1.6 -Deprecate `Randoms.create(String)`, use `Randoms.setSeed(long)` instead. +Deprecate `Randoms.create(String)`, use `Randoms.setSeed(long)` instead. + +Fix an instance generated by `ConstructorPropertiesArbitraryIntrospector` that was not validated by validation annotations. sectionEnd diff --git a/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/instantiator/JavaInstantiatorProcessor.java b/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/instantiator/JavaInstantiatorProcessor.java index 0e3cedd1a..c7f541b93 100644 --- a/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/instantiator/JavaInstantiatorProcessor.java +++ b/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/instantiator/JavaInstantiatorProcessor.java @@ -48,9 +48,9 @@ import com.navercorp.fixturemonkey.api.property.ConstructorPropertyGeneratorContext; import com.navercorp.fixturemonkey.api.property.FieldPropertyGenerator; import com.navercorp.fixturemonkey.api.property.JavaBeansPropertyGenerator; -import com.navercorp.fixturemonkey.api.property.MethodParameterProperty; import com.navercorp.fixturemonkey.api.property.Property; import com.navercorp.fixturemonkey.api.property.PropertyUtils; +import com.navercorp.fixturemonkey.api.property.TypeNameProperty; import com.navercorp.fixturemonkey.api.type.TypeCache; import com.navercorp.fixturemonkey.api.type.TypeReference; import com.navercorp.fixturemonkey.api.type.Types; @@ -239,7 +239,7 @@ private static List getMethodParameterProperties( String resolvedParameterName = resolvedParameterNames.get(i); TypeReference resolvedTypeReference = resolvedParameterTypes.get(i); properties.add( - new MethodParameterProperty( + new TypeNameProperty( resolvedTypeReference.getAnnotatedType(), resolvedParameterName, null diff --git a/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/property/ConstructorParameterPropertyGenerator.java b/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/property/ConstructorParameterPropertyGenerator.java index 1cbedf730..fe29c9594 100644 --- a/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/property/ConstructorParameterPropertyGenerator.java +++ b/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/property/ConstructorParameterPropertyGenerator.java @@ -133,9 +133,8 @@ public List generateParameterProperties(ConstructorPropertyGeneratorCo constructorPropertiesByName.put( receiverParameter.getName(), new ConstructorProperty( - receiverParameter.getAnnotatedType(), + new TypeNameProperty(receiverParameter.getAnnotatedType(), receiverParameter.getName(), null), constructor, - receiverParameter.getName(), null, null ) @@ -158,9 +157,8 @@ public List generateParameterProperties(ConstructorPropertyGeneratorCo constructorPropertiesByName.put( parameterName, new ConstructorProperty( - fieldProperty.getAnnotatedType(), + fieldProperty, constructor, - parameterName, fieldProperty, null ) @@ -169,9 +167,8 @@ public List generateParameterProperties(ConstructorPropertyGeneratorCo constructorPropertiesByName.put( parameterName, new ConstructorProperty( - parameterAnnotatedType, + new TypeNameProperty(parameterAnnotatedType, parameterName, null), constructor, - parameterName, fieldProperty, null ) diff --git a/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/property/ConstructorProperty.java b/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/property/ConstructorProperty.java index fe6612ade..a2bb1adf6 100644 --- a/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/property/ConstructorProperty.java +++ b/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/property/ConstructorProperty.java @@ -22,7 +22,7 @@ import java.lang.reflect.AnnotatedType; import java.lang.reflect.Constructor; import java.lang.reflect.Type; -import java.util.Arrays; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; @@ -37,9 +37,8 @@ @API(since = "0.4.2", status = Status.MAINTAINED) public final class ConstructorProperty implements Property { - private final AnnotatedType annotatedType; + private final Property parameterProperty; private final Constructor constructor; - private final String parameterName; @Nullable private final Property fieldProperty; private final List annotations; @@ -47,6 +46,11 @@ public final class ConstructorProperty implements Property { @Nullable private final Boolean nullable; + /** + * It is deprecated. + * Use {@link #ConstructorProperty(Property, Constructor, Property, Boolean)} instead. + */ + @Deprecated public ConstructorProperty( AnnotatedType annotatedType, Constructor constructor, @@ -54,11 +58,22 @@ public ConstructorProperty( @Nullable Property fieldProperty, @Nullable Boolean nullable ) { - this.annotatedType = annotatedType; + this(new TypeNameProperty(annotatedType, parameterName, nullable), constructor, fieldProperty, nullable); + } + + public ConstructorProperty( + Property parameterProperty, + Constructor constructor, + @Nullable Property fieldProperty, + @Nullable Boolean nullable + ) { + this.parameterProperty = parameterProperty; this.constructor = constructor; - this.parameterName = parameterName; this.fieldProperty = fieldProperty; - this.annotations = Arrays.asList(annotatedType.getAnnotations()); + this.annotations = new ArrayList<>(parameterProperty.getAnnotations()); + if (fieldProperty != null) { + this.annotations.addAll(fieldProperty.getAnnotations()); + } this.annotationsMap = this.annotations.stream() .collect(Collectors.toMap(Annotation::annotationType, Function.identity(), (a1, a2) -> a1)); this.nullable = nullable; @@ -71,7 +86,7 @@ public Type getType() { @Override public AnnotatedType getAnnotatedType() { - return this.annotatedType; + return this.parameterProperty.getAnnotatedType(); } public Constructor getConstructor() { @@ -85,7 +100,7 @@ public Property getFieldProperty() { @Override public String getName() { - return this.parameterName; + return this.parameterProperty.getName(); } @Override @@ -102,7 +117,11 @@ public Optional getAnnotation(Class annotationClass @Nullable @Override public Boolean isNullable() { - return nullable; + if (nullable != null) { + return nullable; + } + + return parameterProperty.isNullable(); } @Override @@ -114,16 +133,15 @@ public boolean equals(Object obj) { return false; } ConstructorProperty that = (ConstructorProperty)obj; - return annotatedType.getType().equals(that.annotatedType.getType()) + return parameterProperty.equals(that.parameterProperty.getType()) && constructor.equals(that.constructor) - && parameterName.equals(that.parameterName) && Objects.equals(fieldProperty, that.fieldProperty) && annotations.equals(that.annotations); } @Override public int hashCode() { - return Objects.hash(annotatedType.getType(), constructor, parameterName, fieldProperty, annotations); + return Objects.hash(parameterProperty, constructor, fieldProperty, annotations); } @Nullable @@ -139,10 +157,12 @@ public Object getValue(Object instance) { @Override public String toString() { return "ConstructorProperty{" - + "annotatedType=" + annotatedType + + "parameterProperty=" + parameterProperty + ", constructor=" + constructor - + ", parameterName='" + parameterName + '\'' + ", fieldProperty=" + fieldProperty + + ", annotations=" + annotations + + ", annotationsMap=" + annotationsMap + + ", nullable=" + nullable + '}'; } } diff --git a/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/property/MethodParameterProperty.java b/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/property/MethodParameterProperty.java index a5fa32f44..5688eabfa 100644 --- a/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/property/MethodParameterProperty.java +++ b/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/property/MethodParameterProperty.java @@ -26,6 +26,11 @@ import javax.annotation.Nullable; +/** + * It is deprecated. + * Use {@link TypeNameProperty} instead. + */ +@Deprecated public final class MethodParameterProperty implements Property { private final AnnotatedType annotatedType; @Nullable diff --git a/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/property/TypeNameProperty.java b/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/property/TypeNameProperty.java new file mode 100644 index 000000000..3160f6de2 --- /dev/null +++ b/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/property/TypeNameProperty.java @@ -0,0 +1,105 @@ +/* + * Fixture Monkey + * + * Copyright (c) 2021-present NAVER Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.navercorp.fixturemonkey.api.property; + +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedType; +import java.lang.reflect.Type; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +import javax.annotation.Nullable; + +import org.apiguardian.api.API; +import org.apiguardian.api.API.Status; + +/** + * It is a property has a type name. + * For example, It can be used to represent a type name of a class or method. + */ +@API(since = "1.1.6", status = Status.EXPERIMENTAL) +public final class TypeNameProperty implements Property { + private final AnnotatedType annotatedType; + private final String typeName; + @Nullable + private final Boolean nullable; + + public TypeNameProperty( + AnnotatedType annotatedType, + String typeName, + @Nullable Boolean nullable + ) { + this.annotatedType = annotatedType; + this.typeName = typeName; + this.nullable = nullable; + } + + @Override + public Type getType() { + return annotatedType.getType(); + } + + @Override + public AnnotatedType getAnnotatedType() { + return annotatedType; + } + + @Nullable + @Override + public String getName() { + return typeName; + } + + @Override + public List getAnnotations() { + return Arrays.asList(annotatedType.getAnnotations()); + } + + @Nullable + @Override + public Object getValue(Object instance) { + throw new UnsupportedOperationException(); + } + + @Nullable + @Override + public Boolean isNullable() { + return nullable; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + TypeNameProperty that = (TypeNameProperty)obj; + return Objects.equals(annotatedType, that.annotatedType) + && Objects.equals(typeName, that.typeName) + && Objects.equals(nullable, that.nullable); + } + + @Override + public int hashCode() { + return Objects.hash(annotatedType, typeName, nullable); + } +} diff --git a/fixture-monkey-kotlin/src/main/kotlin/com/navercorp/fixturemonkey/kotlin/instantiator/KotlinInstantiator.kt b/fixture-monkey-kotlin/src/main/kotlin/com/navercorp/fixturemonkey/kotlin/instantiator/KotlinInstantiator.kt index ccb9b6396..802f57fc0 100644 --- a/fixture-monkey-kotlin/src/main/kotlin/com/navercorp/fixturemonkey/kotlin/instantiator/KotlinInstantiator.kt +++ b/fixture-monkey-kotlin/src/main/kotlin/com/navercorp/fixturemonkey/kotlin/instantiator/KotlinInstantiator.kt @@ -37,9 +37,9 @@ import com.navercorp.fixturemonkey.api.introspector.CompositeArbitraryIntrospect import com.navercorp.fixturemonkey.api.introspector.FieldReflectionArbitraryIntrospector import com.navercorp.fixturemonkey.api.property.FieldPropertyGenerator import com.navercorp.fixturemonkey.api.property.JavaBeansPropertyGenerator -import com.navercorp.fixturemonkey.api.property.MethodParameterProperty import com.navercorp.fixturemonkey.api.property.Property import com.navercorp.fixturemonkey.api.property.PropertyUtils +import com.navercorp.fixturemonkey.api.property.TypeNameProperty import com.navercorp.fixturemonkey.api.type.TypeReference import com.navercorp.fixturemonkey.api.type.Types import com.navercorp.fixturemonkey.kotlin.introspector.CompanionObjectFactoryMethodIntrospector @@ -217,7 +217,7 @@ class KotlinInstantiatorProcessor : ): List = this.parameters .filter { parameter -> parameter.kind != KParameter.Kind.INSTANCE } .mapIndexed { index, kParameter -> - MethodParameterProperty( + TypeNameProperty( resolvedParameterTypes[index].annotatedType, resolvedParameterNames[index], kParameter.type.isMarkedNullable, diff --git a/fixture-monkey-tests/java-tests/src/test/java/com/navercorp/fixturemonkey/tests/java/ConstructorTestSpecs.java b/fixture-monkey-tests/java-tests/src/test/java/com/navercorp/fixturemonkey/tests/java/ConstructorTestSpecs.java index 3fd53a018..0a72e0cf3 100644 --- a/fixture-monkey-tests/java-tests/src/test/java/com/navercorp/fixturemonkey/tests/java/ConstructorTestSpecs.java +++ b/fixture-monkey-tests/java-tests/src/test/java/com/navercorp/fixturemonkey/tests/java/ConstructorTestSpecs.java @@ -10,6 +10,11 @@ import java.util.OptionalLong; import java.util.Set; +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; + +import lombok.AllArgsConstructor; +import lombok.Getter; import lombok.Value; import com.navercorp.fixturemonkey.tests.java.ImmutableJavaTestSpecs.Enum; @@ -239,4 +244,12 @@ public String getValue() { return value; } } + + @Getter + @AllArgsConstructor + public class JavaxValidationObject { + @Max(100) + @Min(100) + private int value; + } } diff --git a/fixture-monkey-tests/java-tests/src/test/java/com/navercorp/fixturemonkey/tests/java/JavaTest.java b/fixture-monkey-tests/java-tests/src/test/java/com/navercorp/fixturemonkey/tests/java/JavaTest.java index 429a1b6de..ffb20eef6 100644 --- a/fixture-monkey-tests/java-tests/src/test/java/com/navercorp/fixturemonkey/tests/java/JavaTest.java +++ b/fixture-monkey-tests/java-tests/src/test/java/com/navercorp/fixturemonkey/tests/java/JavaTest.java @@ -65,10 +65,12 @@ import com.navercorp.fixturemonkey.api.type.TypeReference; import com.navercorp.fixturemonkey.customizer.InnerSpec; import com.navercorp.fixturemonkey.customizer.Values; +import com.navercorp.fixturemonkey.javax.validation.plugin.JavaxValidationPlugin; import com.navercorp.fixturemonkey.resolver.ArbitraryBuilderCandidateFactory; import com.navercorp.fixturemonkey.resolver.ArbitraryBuilderCandidateList; import com.navercorp.fixturemonkey.tests.java.ConstructorAndPropertyTestSpecs.ConsturctorAndProperty; import com.navercorp.fixturemonkey.tests.java.ConstructorTestSpecs.FieldAndConstructorParameterMismatchObject; +import com.navercorp.fixturemonkey.tests.java.ConstructorTestSpecs.JavaxValidationObject; import com.navercorp.fixturemonkey.tests.java.ConstructorTestSpecs.SimpleContainerObject; import com.navercorp.fixturemonkey.tests.java.ImmutableDepthTestSpecs.DepthStringValueList; import com.navercorp.fixturemonkey.tests.java.ImmutableDepthTestSpecs.OneDepthStringValue; @@ -1395,4 +1397,17 @@ void registerJavaTypebuilder() { then(actual).isEqualTo(expected); } + + @RepeatedTest(TEST_COUNT) + void constructorValidator() { + FixtureMonkey sut = FixtureMonkey.builder() + .plugin(new JavaxValidationPlugin()) + .objectIntrospector(ConstructorPropertiesArbitraryIntrospector.INSTANCE) + .defaultNotNull(true) + .build(); + + JavaxValidationObject actual = sut.giveMeOne(JavaxValidationObject.class); + + then(actual.getValue()).isEqualTo(100); + } }