diff --git a/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/matcher/MatcherOperator.java b/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/matcher/MatcherOperator.java index 48a39f1bb..0e293f122 100644 --- a/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/matcher/MatcherOperator.java +++ b/fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/matcher/MatcherOperator.java @@ -24,7 +24,7 @@ import com.navercorp.fixturemonkey.api.property.Property; @API(since = "0.4.0", status = Status.MAINTAINED) -public final class MatcherOperator implements Matcher { +public class MatcherOperator implements Matcher { private final Matcher matcher; private final T operator; diff --git a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/FixtureMonkey.java b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/FixtureMonkey.java index 1d6a0af15..a2a608ec4 100644 --- a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/FixtureMonkey.java +++ b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/FixtureMonkey.java @@ -21,6 +21,8 @@ import static java.util.stream.Collectors.toList; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.function.Function; @@ -36,10 +38,12 @@ import com.navercorp.fixturemonkey.api.matcher.NamedMatcher; import com.navercorp.fixturemonkey.api.option.FixtureMonkeyOptions; import com.navercorp.fixturemonkey.api.property.RootProperty; +import com.navercorp.fixturemonkey.api.random.Randoms; import com.navercorp.fixturemonkey.api.type.LazyAnnotatedType; import com.navercorp.fixturemonkey.api.type.TypeReference; import com.navercorp.fixturemonkey.customizer.ArbitraryManipulator; import com.navercorp.fixturemonkey.customizer.MonkeyManipulatorFactory; +import com.navercorp.fixturemonkey.customizer.PriorityMatcherOperator; import com.navercorp.fixturemonkey.experimental.ExperimentalArbitraryBuilder; import com.navercorp.fixturemonkey.resolver.ArbitraryBuilderContext; import com.navercorp.fixturemonkey.resolver.ArbitraryResolver; @@ -53,7 +57,8 @@ public final class FixtureMonkey { private final ArbitraryTraverser traverser; private final ManipulatorOptimizer manipulatorOptimizer; private final MonkeyContext monkeyContext; - private final List>> registeredArbitraryBuilders = new ArrayList<>(); + private final List>> registeredArbitraryBuilders + = new ArrayList<>(); private final MonkeyManipulatorFactory monkeyManipulatorFactory; public FixtureMonkey( @@ -61,17 +66,19 @@ public FixtureMonkey( ArbitraryTraverser traverser, ManipulatorOptimizer manipulatorOptimizer, MonkeyContext monkeyContext, - List>>> registeredArbitraryBuilders, + List>>> registeredArbitraryBuildersWithPriority, MonkeyManipulatorFactory monkeyManipulatorFactory, - Map>>> mapsByRegisteredName + Map>>> registeredPriorityMatchersByName ) { this.fixtureMonkeyOptions = fixtureMonkeyOptions; this.traverser = traverser; this.manipulatorOptimizer = manipulatorOptimizer; this.monkeyContext = monkeyContext; this.monkeyManipulatorFactory = monkeyManipulatorFactory; - initializeRegisteredArbitraryBuilders(registeredArbitraryBuilders); - initializeNamedArbitraryBuilderMap(mapsByRegisteredName); + initializeRegisteredArbitraryBuilders(registeredArbitraryBuildersWithPriority); + initializeNamedArbitraryBuilderMap(registeredPriorityMatchersByName); } public static FixtureMonkeyBuilder builder() { @@ -91,8 +98,20 @@ public ArbitraryBuilder giveMeBuilder(Class type) { public ArbitraryBuilder giveMeBuilder(TypeReference type) { RootProperty rootProperty = new RootProperty(type.getAnnotatedType()); - ArbitraryBuilderContext builderContext = registeredArbitraryBuilders.stream() + List>> priorityOperators = registeredArbitraryBuilders + .stream() .filter(it -> it.match(rootProperty)) + .sorted(Comparator.comparingInt(PriorityMatcherOperator::getPriority)) + .collect(toList()); + + List>> highestPriorityOperators + = getHighestPriorityOperators(priorityOperators); + + if (highestPriorityOperators.size() > 1) { + Collections.shuffle(highestPriorityOperators, Randoms.current()); + } + + ArbitraryBuilderContext builderContext = highestPriorityOperators.stream() .map(MatcherOperator::getOperator) .findAny() .map(DefaultArbitraryBuilder.class::cast) @@ -120,6 +139,20 @@ public ArbitraryBuilder giveMeBuilder(TypeReference type) { ); } + private List>> getHighestPriorityOperators( + List>> priorityOperators + ) { + if (priorityOperators.isEmpty()) { + return priorityOperators; + } + + int highestPriority = priorityOperators.get(0).getPriority(); + + return priorityOperators.stream() + .filter(it -> it.getPriority() == highestPriority) + .collect(toList()); + } + public ArbitraryBuilder giveMeBuilder(T value) { ArbitraryBuilderContext context = new ArbitraryBuilderContext(); @@ -189,11 +222,14 @@ public Arbitrary giveMeArbitrary(TypeReference typeReference) { } private void initializeRegisteredArbitraryBuilders( - List>>> registeredArbitraryBuilders + List>>> registeredArbitraryBuildersWithPriority ) { - List>> generatedRegisteredArbitraryBuilder = - registeredArbitraryBuilders.stream() - .map(it -> new MatcherOperator<>(it.getMatcher(), it.getOperator().apply(this))) + List>> generatedRegisteredArbitraryBuilder = + registeredArbitraryBuildersWithPriority.stream() + .map(it -> new PriorityMatcherOperator<>( + it.getMatcher(), it.getOperator().apply(this), it.getPriority()) + ) .collect(toList()); for (int i = generatedRegisteredArbitraryBuilder.size() - 1; i >= 0; i--) { @@ -202,13 +238,15 @@ private void initializeRegisteredArbitraryBuilders( } private void initializeNamedArbitraryBuilderMap( - Map>>> mapsByRegisteredName + Map>>> mapsByRegisteredName ) { mapsByRegisteredName.forEach((registeredName, matcherOperator) -> { registeredArbitraryBuilders.add( - new MatcherOperator<>( + new PriorityMatcherOperator<>( new NamedMatcher(matcherOperator.getMatcher(), registeredName), - matcherOperator.getOperator().apply(this) + matcherOperator.getOperator().apply(this), + matcherOperator.getPriority() ) ); }); diff --git a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/FixtureMonkeyBuilder.java b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/FixtureMonkeyBuilder.java index 8b3db271f..ec67952d0 100644 --- a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/FixtureMonkeyBuilder.java +++ b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/FixtureMonkeyBuilder.java @@ -32,6 +32,7 @@ import org.apiguardian.api.API; import org.apiguardian.api.API.Status; +import com.navercorp.fixturemonkey.annotation.Order; import com.navercorp.fixturemonkey.api.constraint.JavaConstraintGenerator; import com.navercorp.fixturemonkey.api.container.DecomposedContainerValueFactory; import com.navercorp.fixturemonkey.api.context.MonkeyContext; @@ -62,6 +63,7 @@ import com.navercorp.fixturemonkey.buildergroup.ArbitraryBuilderCandidate; import com.navercorp.fixturemonkey.buildergroup.ArbitraryBuilderGroup; import com.navercorp.fixturemonkey.customizer.MonkeyManipulatorFactory; +import com.navercorp.fixturemonkey.customizer.PriorityMatcherOperator; import com.navercorp.fixturemonkey.expression.ArbitraryExpressionFactory; import com.navercorp.fixturemonkey.expression.MonkeyExpressionFactory; import com.navercorp.fixturemonkey.resolver.ManipulatorOptimizer; @@ -72,14 +74,16 @@ @SuppressWarnings("unused") @API(since = "0.4.0", status = Status.MAINTAINED) public final class FixtureMonkeyBuilder { + private static final int DEFAULT_PRIORITY = Integer.MAX_VALUE; + private final FixtureMonkeyOptionsBuilder fixtureMonkeyOptionsBuilder = FixtureMonkeyOptions.builder(); - private final List>>> - registeredArbitraryBuilders = new ArrayList<>(); + private final List>>> + registeredArbitraryBuildersWithPriority = new ArrayList<>(); private ManipulatorOptimizer manipulatorOptimizer = new NoneManipulatorOptimizer(); private MonkeyExpressionFactory monkeyExpressionFactory = new ArbitraryExpressionFactory(); private final MonkeyContextBuilder monkeyContextBuilder = MonkeyContext.builder(); - private final Map>>> - registeredArbitraryListByRegisteredName = new HashMap<>(); + private final Map>>> + registeredPriorityMatchersByName = new HashMap<>(); private long seed = System.nanoTime(); // The default plugins are listed below. @@ -310,11 +314,37 @@ public FixtureMonkeyBuilder addExceptGeneratePackages(String... exceptGeneratePa return this; } + /** + * Registers an ArbitraryBuilder with the DEFAULT priority (Integer.MAX_VALUE). + * + * @param registeredArbitraryBuilder the MatcherOperator containing the matcher + * and the ArbitraryBuilder to be registered + * @return the current instance of FixtureMonkeyBuilder for method chaining + */ public FixtureMonkeyBuilder register( Class type, Function> registeredArbitraryBuilder ) { - return this.register(MatcherOperator.assignableTypeMatchOperator(type, registeredArbitraryBuilder)); + return this.register(type, registeredArbitraryBuilder, DEFAULT_PRIORITY); + } + + /** + * Registers an ArbitraryBuilder with a specified priority. + * + * @param registeredArbitraryBuilder the MatcherOperator containing the matcher + * and the ArbitraryBuilder to be registered + * @param priority the priority of the ArbitraryBuilder; higher values indicate lower priority + * @return the current instance of FixtureMonkeyBuilder for method chaining + * @throws IllegalArgumentException if the priority is less than 0 + * + * If multiple ArbitraryBuilders have the same priority, one of them will be selected randomly. + */ + public FixtureMonkeyBuilder register( + Class type, + Function> registeredArbitraryBuilder, + int priority + ) { + return this.register(MatcherOperator.assignableTypeMatchOperator(type, registeredArbitraryBuilder), priority); } public FixtureMonkeyBuilder registerExactType( @@ -334,7 +364,18 @@ public FixtureMonkeyBuilder registerAssignableType( public FixtureMonkeyBuilder register( MatcherOperator>> registeredArbitraryBuilder ) { - this.registeredArbitraryBuilders.add(registeredArbitraryBuilder); + return this.register(registeredArbitraryBuilder, DEFAULT_PRIORITY); + } + + public FixtureMonkeyBuilder register( + MatcherOperator>> registeredArbitraryBuilder, + int priority + ) { + this.registeredArbitraryBuildersWithPriority.add( + new PriorityMatcherOperator<>( + registeredArbitraryBuilder.getMatcher(), registeredArbitraryBuilder.getOperator(), priority + ) + ); return this; } @@ -361,6 +402,11 @@ public FixtureMonkeyBuilder registerGroup(Class... arbitraryBuilderGroups) { throw new RuntimeException(ex); } }; + + if (arbitraryBuilderGroup.isAnnotationPresent(Order.class)) { + Order order = arbitraryBuilderGroup.getAnnotation(Order.class); + this.register(actualType, registerArbitraryBuilder, order.value()); + } this.register(actualType, registerArbitraryBuilder); } catch (Exception ex) { // ignored @@ -372,15 +418,24 @@ public FixtureMonkeyBuilder registerGroup(Class... arbitraryBuilderGroups) { public FixtureMonkeyBuilder registerGroup(ArbitraryBuilderGroup... arbitraryBuilderGroups) { for (ArbitraryBuilderGroup arbitraryBuilderGroup : arbitraryBuilderGroups) { - List> candidates = arbitraryBuilderGroup.generateCandidateList() - .getCandidates(); - - for (ArbitraryBuilderCandidate candidate : candidates) { - this.register( - candidate.getClassType(), - candidate.getArbitraryBuilderRegisterer() - ); - } + this.registerGroup(arbitraryBuilderGroup, DEFAULT_PRIORITY); + } + return this; + } + + public FixtureMonkeyBuilder registerGroup( + ArbitraryBuilderGroup arbitraryBuilderGroup, + int priority + ) { + List> candidates = arbitraryBuilderGroup.generateCandidateList() + .getCandidates(); + + for (ArbitraryBuilderCandidate candidate : candidates) { + this.register( + candidate.getClassType(), + candidate.getArbitraryBuilderRegisterer(), + priority + ); } return this; } @@ -390,13 +445,26 @@ public FixtureMonkeyBuilder registeredName( Class type, Function> arbitraryBuilder ) { - if (registeredArbitraryListByRegisteredName.containsKey(registeredName)) { + return this.registeredName(registeredName, type, arbitraryBuilder, DEFAULT_PRIORITY); + } + + public FixtureMonkeyBuilder registeredName( + String registeredName, + Class type, + Function> arbitraryBuilder, + int priority + ) { + if (registeredPriorityMatchersByName.containsKey(registeredName)) { throw new IllegalArgumentException("Duplicated ArbitraryBuilder name: " + registeredName); } MatcherOperator>> matcherOperator = MatcherOperator.assignableTypeMatchOperator(type, arbitraryBuilder); - this.registeredArbitraryListByRegisteredName.put(registeredName, matcherOperator); + this.registeredPriorityMatchersByName.put( + registeredName, new PriorityMatcherOperator<>( + matcherOperator.getMatcher(), matcherOperator.getOperator(), priority + ) + ); return this; } @@ -570,9 +638,9 @@ public FixtureMonkey build() { traverser, manipulatorOptimizer, monkeyContext, - registeredArbitraryBuilders, + registeredArbitraryBuildersWithPriority, monkeyManipulatorFactory, - registeredArbitraryListByRegisteredName + registeredPriorityMatchersByName ); } } diff --git a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/annotation/Order.java b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/annotation/Order.java new file mode 100644 index 000000000..90e34898e --- /dev/null +++ b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/annotation/Order.java @@ -0,0 +1,35 @@ +/* + * 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.annotation; + +import static org.apiguardian.api.API.Status.EXPERIMENTAL; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.apiguardian.api.API; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE}) +@API(since = "1.1.10", status = EXPERIMENTAL) +public @interface Order { + int value() default 0; +} diff --git a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/customizer/MonkeyManipulatorFactory.java b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/customizer/MonkeyManipulatorFactory.java index 82a41f248..c9a185379 100644 --- a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/customizer/MonkeyManipulatorFactory.java +++ b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/customizer/MonkeyManipulatorFactory.java @@ -18,8 +18,11 @@ package com.navercorp.fixturemonkey.customizer; +import static java.util.stream.Collectors.toList; + import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -44,6 +47,7 @@ import com.navercorp.fixturemonkey.api.matcher.MatcherOperator; import com.navercorp.fixturemonkey.api.matcher.NamedMatcherMetadata; import com.navercorp.fixturemonkey.api.property.Property; +import com.navercorp.fixturemonkey.api.random.Randoms; import com.navercorp.fixturemonkey.customizer.InnerSpecState.ManipulatorHolderSet; import com.navercorp.fixturemonkey.customizer.Values.Just; import com.navercorp.fixturemonkey.expression.MonkeyExpressionFactory; @@ -142,7 +146,7 @@ public ContainerInfoManipulator newContainerInfoManipulator( } public List newRegisteredArbitraryManipulators( - List>> registeredArbitraryBuilders, + List>> registeredArbitraryBuilders, Map> nodesByType, ArbitraryBuilderContext builderContext ) { @@ -152,20 +156,9 @@ public List newRegisteredArbitraryManipulators( Property property = nodeByType.getKey(); List objectNodes = nodeByType.getValue(); - DefaultArbitraryBuilder registeredArbitraryBuilder = - (DefaultArbitraryBuilder)registeredArbitraryBuilders.stream() - .filter(it -> { - if (builderContext.getSelectedNames().isEmpty()) { - return it.match(property); - } - return builderContext.getSelectedNames().stream().anyMatch( - name -> it.match(property, new NamedMatcherMetadata(name)) - ); - }) - .findFirst() - .map(MatcherOperator::getOperator) - .filter(it -> it instanceof DefaultArbitraryBuilder) - .orElse(null); + DefaultArbitraryBuilder registeredArbitraryBuilder = findRegisteredArbitraryBuilder( + registeredArbitraryBuilders, property, builderContext + ); if (registeredArbitraryBuilder == null) { continue; @@ -181,6 +174,64 @@ public List newRegisteredArbitraryManipulators( return manipulators; } + private DefaultArbitraryBuilder findRegisteredArbitraryBuilder( + List>> registeredArbitraryBuilders, + Property property, + ArbitraryBuilderContext builderContext + ) { + DefaultArbitraryBuilder registeredArbitraryBuilder = (DefaultArbitraryBuilder)registeredArbitraryBuilders + .stream() + .filter(it -> { + if (builderContext.getSelectedNames().isEmpty()) { + return false; + } + return builderContext.getSelectedNames().stream().anyMatch( + name -> it.match(property, new NamedMatcherMetadata(name)) + ); + }) + .findFirst() + .map(MatcherOperator::getOperator) + .filter(it -> it instanceof DefaultArbitraryBuilder) + .orElse(null); + + if (registeredArbitraryBuilder != null) { + return registeredArbitraryBuilder; + } + + List>> priorityOperators = registeredArbitraryBuilders + .stream() + .filter(it -> it.match(property)) + .sorted(Comparator.comparingInt(PriorityMatcherOperator::getPriority)) + .collect(Collectors.toList()); + + List>> highestPriorityOperators + = getHighestPriorityOperators(priorityOperators); + + if (highestPriorityOperators.size() > 1) { + Collections.shuffle(highestPriorityOperators, Randoms.current()); + } + + return (DefaultArbitraryBuilder)highestPriorityOperators.stream() + .findFirst() + .map(MatcherOperator::getOperator) + .filter(it -> it instanceof DefaultArbitraryBuilder) + .orElse(null); + } + + private List>> getHighestPriorityOperators( + List>> priorityOperators + ) { + if (priorityOperators.isEmpty()) { + return priorityOperators; + } + + int highestPriority = priorityOperators.get(0).getPriority(); + + return priorityOperators.stream() + .filter(it -> it.getPriority() == highestPriority) + .collect(toList()); + } + public ManipulatorSet newManipulatorSet(ManipulatorHolderSet manipulatorHolderSet) { int baseSequence = sequence.getAndIncrement(); diff --git a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/customizer/PriorityMatcherOperator.java b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/customizer/PriorityMatcherOperator.java new file mode 100644 index 000000000..aa69cd953 --- /dev/null +++ b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/customizer/PriorityMatcherOperator.java @@ -0,0 +1,69 @@ +/* + * 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.customizer; + +import static org.apiguardian.api.API.Status.EXPERIMENTAL; + +import org.apiguardian.api.API; + +import com.navercorp.fixturemonkey.api.matcher.Matcher; +import com.navercorp.fixturemonkey.api.matcher.MatcherOperator; + +/** + * A class that represents a matcher operator with a priority. + * This class extends {@link MatcherOperator} and adds a priority field. + * + * @param the type of the operator + * @since 1.1.10 + */ +@API(since = "1.1.10", status = EXPERIMENTAL) +public final class PriorityMatcherOperator extends MatcherOperator { + private final int priority; + + public PriorityMatcherOperator( + Matcher matcher, + T operator, + int priority + ) { + super(matcher, operator); + checkPriority(priority); + this.priority = priority; + } + + /** + * Returns the priority of this matcher operator. + * + * @return the priority of this matcher operator + */ + public int getPriority() { + return priority; + } + + /** + * Checks if the priority is valid. + * + * @param priority the priority to be checked + * @throws IllegalArgumentException if the priority is less than 0 + */ + private void checkPriority(int priority) { + if (priority < 0) { + throw new IllegalArgumentException("Priority must be greater than or equal to 0"); + } + } +} diff --git a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/resolver/ArbitraryResolver.java b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/resolver/ArbitraryResolver.java index 9b3931816..218e16091 100644 --- a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/resolver/ArbitraryResolver.java +++ b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/resolver/ArbitraryResolver.java @@ -37,6 +37,7 @@ import com.navercorp.fixturemonkey.customizer.ArbitraryManipulator; import com.navercorp.fixturemonkey.customizer.ContainerInfoManipulator; import com.navercorp.fixturemonkey.customizer.MonkeyManipulatorFactory; +import com.navercorp.fixturemonkey.customizer.PriorityMatcherOperator; import com.navercorp.fixturemonkey.tree.ArbitraryTraverser; import com.navercorp.fixturemonkey.tree.ObjectTree; @@ -47,7 +48,7 @@ public final class ArbitraryResolver { private final MonkeyManipulatorFactory monkeyManipulatorFactory; private final FixtureMonkeyOptions fixtureMonkeyOptions; private final MonkeyContext monkeyContext; - private final List>> registeredArbitraryBuilders; + private final List>> registeredArbitraryBuilders; public ArbitraryResolver( ArbitraryTraverser traverser, @@ -55,7 +56,7 @@ public ArbitraryResolver( MonkeyManipulatorFactory monkeyManipulatorFactory, FixtureMonkeyOptions fixtureMonkeyOptions, MonkeyContext monkeyContext, - List>> registeredArbitraryBuilders + List>> registeredArbitraryBuilders ) { this.traverser = traverser; this.manipulatorOptimizer = manipulatorOptimizer; diff --git a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/resolver/DefaultArbitraryBuilder.java b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/resolver/DefaultArbitraryBuilder.java index f1ac45d97..30ac85dae 100644 --- a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/resolver/DefaultArbitraryBuilder.java +++ b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/resolver/DefaultArbitraryBuilder.java @@ -72,6 +72,7 @@ import com.navercorp.fixturemonkey.customizer.InnerSpec; import com.navercorp.fixturemonkey.customizer.ManipulatorSet; import com.navercorp.fixturemonkey.customizer.MonkeyManipulatorFactory; +import com.navercorp.fixturemonkey.customizer.PriorityMatcherOperator; import com.navercorp.fixturemonkey.experimental.ExperimentalArbitraryBuilder; import com.navercorp.fixturemonkey.tree.ArbitraryTraverser; @@ -84,7 +85,7 @@ public final class DefaultArbitraryBuilder implements ArbitraryBuilder, Ex private final ArbitraryTraverser traverser; private final MonkeyManipulatorFactory monkeyManipulatorFactory; private final ArbitraryBuilderContext context; - private final List>> registeredArbitraryBuilders; + private final List>> registeredArbitraryBuilders; private final ManipulatorOptimizer manipulatorOptimizer; private final MonkeyContext monkeyContext; private final InstantiatorProcessor instantiatorProcessor; @@ -96,7 +97,7 @@ public DefaultArbitraryBuilder( ArbitraryTraverser traverser, MonkeyManipulatorFactory monkeyManipulatorFactory, ArbitraryBuilderContext context, - List>> registeredArbitraryBuilders, + List>> registeredArbitraryBuilders, MonkeyContext monkeyContext, ManipulatorOptimizer manipulatorOptimizer, InstantiatorProcessor instantiatorProcessor @@ -183,6 +184,7 @@ public ArbitraryBuilder setLazy(PropertySelector propertySelector, Supplier selectName(String... names) { ArbitraryBuilderContext builderContext = registeredArbitraryBuilders.stream() .filter(operator -> Arrays.stream(names) diff --git a/fixture-monkey/src/test/java/com/navercorp/fixturemonkey/test/FixtureMonkeyOptionsAdditionalTestSpecs.java b/fixture-monkey/src/test/java/com/navercorp/fixturemonkey/test/FixtureMonkeyOptionsAdditionalTestSpecs.java index d87896af7..4d5d9c35b 100644 --- a/fixture-monkey/src/test/java/com/navercorp/fixturemonkey/test/FixtureMonkeyOptionsAdditionalTestSpecs.java +++ b/fixture-monkey/src/test/java/com/navercorp/fixturemonkey/test/FixtureMonkeyOptionsAdditionalTestSpecs.java @@ -36,6 +36,7 @@ import com.navercorp.fixturemonkey.ArbitraryBuilder; import com.navercorp.fixturemonkey.FixtureMonkey; +import com.navercorp.fixturemonkey.annotation.Order; import com.navercorp.fixturemonkey.api.arbitrary.CombinableArbitrary; import com.navercorp.fixturemonkey.api.generator.ArbitraryContainerInfo; import com.navercorp.fixturemonkey.api.generator.ArbitraryGenerator; @@ -101,6 +102,29 @@ public ArbitraryBuilder concreteIntValue(FixtureMonkey fixture } } + @Order(value = 1) + public static class RegisterGroupWithPriority { + public static final ConcreteIntValue FIXED_INT_VALUE = new ConcreteIntValue(); + + public ArbitraryBuilder string(FixtureMonkey fixtureMonkey) { + return fixtureMonkey.giveMeBuilder(String.class) + .set(Arbitraries.strings().numeric().ofMinLength(4).ofMaxLength(6)); + } + + public ArbitraryBuilder> stringList(FixtureMonkey fixtureMonkey) { + return fixtureMonkey.giveMeBuilder(new TypeReference>() { + }) + .setInner( + new InnerSpec() + .minSize(5) + ); + } + + public ArbitraryBuilder concreteIntValue(FixtureMonkey fixtureMonkey) { + return fixtureMonkey.giveMeBuilder(FIXED_INT_VALUE); + } + } + public static class ChildBuilderGroup implements ArbitraryBuilderGroup { public static final ConcreteIntValue FIXED_INT_VALUE = new ConcreteIntValue(); @@ -138,6 +162,43 @@ public ArbitraryBuilderCandidateList generateCandidateList() { } } + public static class SiblingBuilderGroup implements ArbitraryBuilderGroup { + public static final ConcreteIntValue FIXED_INT_VALUE = new ConcreteIntValue(); + + @Override + public ArbitraryBuilderCandidateList generateCandidateList() { + return ArbitraryBuilderCandidateList.create() + .add( + ArbitraryBuilderCandidateFactory + .of(String.class) + .builder( + arbitraryBuilder -> arbitraryBuilder.set( + Arbitraries.strings() + .numeric() + .ofMinLength(4) + .ofMaxLength(6) + ) + ) + ) + .add( + ArbitraryBuilderCandidateFactory + .of(new TypeReference>() { + }) + .builder( + builder -> builder.setInner( + new InnerSpec() + .minSize(4) + ) + ) + ) + .add( + ArbitraryBuilderCandidateFactory + .of(ConcreteIntValue.class) + .value(FIXED_INT_VALUE) + ); + } + } + interface PairInterface { S getFirst(); diff --git a/fixture-monkey/src/test/java/com/navercorp/fixturemonkey/test/FixtureMonkeyOptionsTest.java b/fixture-monkey/src/test/java/com/navercorp/fixturemonkey/test/FixtureMonkeyOptionsTest.java index da569add4..75fd87911 100644 --- a/fixture-monkey/src/test/java/com/navercorp/fixturemonkey/test/FixtureMonkeyOptionsTest.java +++ b/fixture-monkey/src/test/java/com/navercorp/fixturemonkey/test/FixtureMonkeyOptionsTest.java @@ -99,8 +99,10 @@ import com.navercorp.fixturemonkey.test.FixtureMonkeyOptionsAdditionalTestSpecs.PairInterface; import com.navercorp.fixturemonkey.test.FixtureMonkeyOptionsAdditionalTestSpecs.PairIntrospector; import com.navercorp.fixturemonkey.test.FixtureMonkeyOptionsAdditionalTestSpecs.RegisterGroup; +import com.navercorp.fixturemonkey.test.FixtureMonkeyOptionsAdditionalTestSpecs.RegisterGroupWithPriority; import com.navercorp.fixturemonkey.test.FixtureMonkeyOptionsAdditionalTestSpecs.SelfRecursiveAbstractValue; import com.navercorp.fixturemonkey.test.FixtureMonkeyOptionsAdditionalTestSpecs.SelfRecursiveImplementationValue; +import com.navercorp.fixturemonkey.test.FixtureMonkeyOptionsAdditionalTestSpecs.SiblingBuilderGroup; import com.navercorp.fixturemonkey.test.FixtureMonkeyOptionsAdditionalTestSpecs.SimpleObjectChild; import com.navercorp.fixturemonkey.test.FixtureMonkeyOptionsAdditionalTestSpecs.UniqueArbitraryGenerator; import com.navercorp.fixturemonkey.test.FixtureMonkeyTestSpecs.ComplexObject; @@ -780,7 +782,6 @@ void registerGroup() { then(actual).hasSizeBetween(1, 3); then(actual2).hasSizeLessThan(5); then(actual3).isEqualTo(RegisterGroup.FIXED_INT_VALUE); - } @Property @@ -859,30 +860,97 @@ void generateSampleListWithRegisteredNames() { } @Property - void registerSameInstancesTwiceWorksLast() { + void registerSetFirst() { + String expected = "test2"; FixtureMonkey sut = FixtureMonkey.builder() .register(String.class, monkey -> monkey.giveMeBuilder("test")) - .register(String.class, monkey -> monkey.giveMeBuilder("test2")) .build(); - String actual = sut.giveMeOne(SimpleObject.class) - .getStr(); + String actual = sut.giveMeBuilder(String.class) + .set("$", expected) + .sample(); - then(actual).isEqualTo("test2"); + then(actual).isEqualTo(expected); } @Property - void registerSetFirst() { - String expected = "test2"; + void registerWithPriority() { FixtureMonkey sut = FixtureMonkey.builder() - .register(String.class, monkey -> monkey.giveMeBuilder("test")) + .register(String.class, monkey -> monkey.giveMeBuilder("test"), 1) + .register(String.class, monkey -> monkey.giveMeBuilder("test2"), 2) .build(); String actual = sut.giveMeBuilder(String.class) - .set("$", expected) .sample(); - then(actual).isEqualTo(expected); + then(actual).isEqualTo("test"); + } + + @Property + void registeredNameWithPriority() { + FixtureMonkey sut = FixtureMonkey.builder() + .registeredName( + "test", + String.class, + monkey -> monkey.giveMeBuilder("test"), + 1 + ) + .registeredName( + "test2", + String.class, + monkey -> monkey.giveMeBuilder("test2"), + 2 + ) + .build(); + + String actual = sut.giveMeBuilder(String.class) + .sample(); + + then(actual).isEqualTo("test"); + } + + @Property + void registerGroupWithPriorityAnnotation() { + FixtureMonkey sut = FixtureMonkey.builder() + .registerGroup(RegisterGroup.class, RegisterGroupWithPriority.class) + .build(); + + String actual = sut.giveMeOne(SimpleObject.class) + .getStr(); + List actual2 = sut.giveMeOne(new TypeReference>() { + }); + ConcreteIntValue actual3 = sut.giveMeOne(ConcreteIntValue.class); + + then(actual).hasSizeBetween(4, 6); + then(actual2).hasSizeGreaterThan(4); + then(actual3).isEqualTo(RegisterGroup.FIXED_INT_VALUE); + } + + @Property + void registerGroupWithPriority() { + FixtureMonkey sut = FixtureMonkey.builder() + .registerGroup(new SiblingBuilderGroup(), 1) + .registerGroup(new ChildBuilderGroup(), 2) + .build(); + + String actual = sut.giveMeOne(SimpleObject.class) + .getStr(); + List actual2 = sut.giveMeOne(new TypeReference>() { + }); + ConcreteIntValue actual3 = sut.giveMeOne(ConcreteIntValue.class); + + then(actual).hasSizeBetween(4, 6); + then(actual2).hasSizeGreaterThan(3); + then(actual3).isEqualTo(ChildBuilderGroup.FIXED_INT_VALUE); + } + + @Property + void registerWithPriorityLessThenZero() { + thenThrownBy(() -> FixtureMonkey.builder() + .register(String.class, monkey -> monkey.giveMeBuilder("test"), -1) + .build() + ).isExactlyInstanceOf(IllegalArgumentException.class) + .hasMessage("Priority must be greater than or equal to 0"); } @Property