diff --git a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/ArbitraryBuilder.java b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/ArbitraryBuilder.java index 595ec3804..32991b34e 100644 --- a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/ArbitraryBuilder.java +++ b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/ArbitraryBuilder.java @@ -112,6 +112,14 @@ public interface ArbitraryBuilder { */ ArbitraryBuilder set(@Nullable Object value); + /** + * Set the {@link ArbitraryBuilder} sampling given {@code names}. + * + * @param names An array of names to select and register their corresponding ArbitraryBuilders. + * @return an {@link ArbitraryBuilder} with the selected properties. + */ + ArbitraryBuilder selectName(String... names); + /** * Apply one or more manipulations defined in {@link InnerSpec}. * 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 9434531b1..0cb031b6d 100644 --- a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/FixtureMonkey.java +++ b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/FixtureMonkey.java @@ -21,7 +21,9 @@ import static java.util.stream.Collectors.toList; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.function.Function; import java.util.stream.Stream; @@ -53,6 +55,8 @@ public final class FixtureMonkey { private final MonkeyContext monkeyContext; private final List>> registeredArbitraryBuilders = new ArrayList<>(); private final MonkeyManipulatorFactory monkeyManipulatorFactory; + private final Map>> + namedArbitraryBuilderMap = new HashMap<>(); public FixtureMonkey( FixtureMonkeyOptions fixtureMonkeyOptions, @@ -60,7 +64,8 @@ public FixtureMonkey( ManipulatorOptimizer manipulatorOptimizer, MonkeyContext monkeyContext, List>>> registeredArbitraryBuilders, - MonkeyManipulatorFactory monkeyManipulatorFactory + MonkeyManipulatorFactory monkeyManipulatorFactory, + Map>>> mapsByRegisteredName ) { this.fixtureMonkeyOptions = fixtureMonkeyOptions; this.traverser = traverser; @@ -68,6 +73,7 @@ public FixtureMonkey( this.monkeyContext = monkeyContext; this.monkeyManipulatorFactory = monkeyManipulatorFactory; initializeRegisteredArbitraryBuilders(registeredArbitraryBuilders); + initializeNamedArbitraryBuilderMap(mapsByRegisteredName); } public static FixtureMonkeyBuilder builder() { @@ -110,7 +116,9 @@ public ArbitraryBuilder giveMeBuilder(TypeReference type) { monkeyManipulatorFactory, builderContext.copy(), registeredArbitraryBuilders, + namedArbitraryBuilderMap, monkeyContext, + manipulatorOptimizer, fixtureMonkeyOptions.getInstantiatorProcessor() ); } @@ -137,7 +145,9 @@ public ArbitraryBuilder giveMeBuilder(T value) { monkeyManipulatorFactory, context, registeredArbitraryBuilders, + namedArbitraryBuilderMap, monkeyContext, + manipulatorOptimizer, fixtureMonkeyOptions.getInstantiatorProcessor() ); } @@ -194,4 +204,14 @@ private void initializeRegisteredArbitraryBuilders( this.registeredArbitraryBuilders.add(generatedRegisteredArbitraryBuilder.get(i)); } } + + private void initializeNamedArbitraryBuilderMap( + Map>>> mapsByRegisteredName + ) { + mapsByRegisteredName.forEach((name, matcherOperator) -> { + namedArbitraryBuilderMap.put( + name, new MatcherOperator<>(matcherOperator.getMatcher(), matcherOperator.getOperator().apply(this)) + ); + }); + } } 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 17da398f4..8b3db271f 100644 --- a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/FixtureMonkeyBuilder.java +++ b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/FixtureMonkeyBuilder.java @@ -22,7 +22,9 @@ import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; import java.util.function.UnaryOperator; @@ -76,6 +78,8 @@ public final class FixtureMonkeyBuilder { private ManipulatorOptimizer manipulatorOptimizer = new NoneManipulatorOptimizer(); private MonkeyExpressionFactory monkeyExpressionFactory = new ArbitraryExpressionFactory(); private final MonkeyContextBuilder monkeyContextBuilder = MonkeyContext.builder(); + private final Map>>> + registeredArbitraryListByRegisteredName = new HashMap<>(); private long seed = System.nanoTime(); // The default plugins are listed below. @@ -381,6 +385,21 @@ public FixtureMonkeyBuilder registerGroup(ArbitraryBuilderGroup... arbitraryBuil return this; } + public FixtureMonkeyBuilder registeredName( + String registeredName, + Class type, + Function> arbitraryBuilder + ) { + if (registeredArbitraryListByRegisteredName.containsKey(registeredName)) { + throw new IllegalArgumentException("Duplicated ArbitraryBuilder name: " + registeredName); + } + MatcherOperator>> matcherOperator = + MatcherOperator.assignableTypeMatchOperator(type, arbitraryBuilder); + + this.registeredArbitraryListByRegisteredName.put(registeredName, matcherOperator); + return this; + } + public FixtureMonkeyBuilder plugin(Plugin plugin) { if (plugin instanceof InterfacePlugin) { // TODO: Added for backward compatibility. It will be removed in 1.1.0 this.defaultInterfacePlugin = (InterfacePlugin)plugin; @@ -552,7 +571,8 @@ public FixtureMonkey build() { manipulatorOptimizer, monkeyContext, registeredArbitraryBuilders, - monkeyManipulatorFactory + monkeyManipulatorFactory, + registeredArbitraryListByRegisteredName ); } } diff --git a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/resolver/ArbitraryBuilderContext.java b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/resolver/ArbitraryBuilderContext.java index 2b9dba45e..ddb69ee30 100644 --- a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/resolver/ArbitraryBuilderContext.java +++ b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/resolver/ArbitraryBuilderContext.java @@ -44,7 +44,6 @@ public final class ArbitraryBuilderContext { private final List containerInfoManipulators; private final Map, List> propertyConfigurers; private final Map, ArbitraryIntrospector> arbitraryIntrospectorsByType; - private boolean validOnly; @Nullable @@ -71,7 +70,8 @@ public ArbitraryBuilderContext( } public ArbitraryBuilderContext() { - this(new ArrayList<>(), new ArrayList<>(), new HashMap<>(), new HashMap<>(), true, null, null); + this( + new ArrayList<>(), new ArrayList<>(), new HashMap<>(), new HashMap<>(), true, null, null); } public ArbitraryBuilderContext copy() { 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 e3db374b2..2d1553059 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 @@ -28,6 +28,7 @@ import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.Consumer; @@ -83,8 +84,10 @@ public final class DefaultArbitraryBuilder implements ArbitraryBuilder, Ex private final MonkeyManipulatorFactory monkeyManipulatorFactory; private final ArbitraryBuilderContext context; private final List>> registeredArbitraryBuilders; + private final ManipulatorOptimizer manipulatorOptimizer; private final MonkeyContext monkeyContext; private final InstantiatorProcessor instantiatorProcessor; + private final Map>> namedArbitraryBuilderMap; public DefaultArbitraryBuilder( FixtureMonkeyOptions fixtureMonkeyOptions, @@ -94,7 +97,9 @@ public DefaultArbitraryBuilder( MonkeyManipulatorFactory monkeyManipulatorFactory, ArbitraryBuilderContext context, List>> registeredArbitraryBuilders, + Map>> registeredArbitraryBuildersByRegsiteredName, MonkeyContext monkeyContext, + ManipulatorOptimizer manipulatorOptimizer, InstantiatorProcessor instantiatorProcessor ) { this.fixtureMonkeyOptions = fixtureMonkeyOptions; @@ -104,6 +109,8 @@ public DefaultArbitraryBuilder( this.context = context; this.monkeyManipulatorFactory = monkeyManipulatorFactory; this.registeredArbitraryBuilders = registeredArbitraryBuilders; + this.namedArbitraryBuilderMap = registeredArbitraryBuildersByRegsiteredName; + this.manipulatorOptimizer = manipulatorOptimizer; this.monkeyContext = monkeyContext; this.instantiatorProcessor = instantiatorProcessor; } @@ -178,6 +185,50 @@ public ArbitraryBuilder setLazy(PropertySelector propertySelector, Supplier selectName(String... names) { + List>> registeredArbitraryBuildersCopy = + new ArrayList<>(this.registeredArbitraryBuilders); + + for (String name : names) { + MatcherOperator> namedArbitraryBuilder = namedArbitraryBuilderMap.get(name); + + if (namedArbitraryBuilder == null) { + throw new IllegalArgumentException("Given name is not registered. name: " + name); + } + registeredArbitraryBuildersCopy.add(namedArbitraryBuilder); + } + + ArbitraryBuilderContext builderContext = registeredArbitraryBuildersCopy.stream() + .filter(it -> it.match(rootProperty)) + .map(MatcherOperator::getOperator) + .findAny() + .map(DefaultArbitraryBuilder.class::cast) + .map(DefaultArbitraryBuilder::getContext) + .orElse(new ArbitraryBuilderContext()); + + return new DefaultArbitraryBuilder<>( + this.fixtureMonkeyOptions, + this.rootProperty, + new ArbitraryResolver( + this.traverser, + this.manipulatorOptimizer, + this.monkeyManipulatorFactory, + this.fixtureMonkeyOptions, + this.monkeyContext, + registeredArbitraryBuildersCopy + ), + this.traverser, + this.monkeyManipulatorFactory, + builderContext.copy(), + registeredArbitraryBuildersCopy, + this.namedArbitraryBuilderMap, + this.monkeyContext, + this.manipulatorOptimizer, + this.fixtureMonkeyOptions.getInstantiatorProcessor() + ); + } + @Override public ArbitraryBuilder setInner(InnerSpec innerSpec) { ManipulatorSet manipulatorSet = innerSpec.getManipulatorSet(monkeyManipulatorFactory); @@ -509,7 +560,9 @@ public ArbitraryBuilder copy() { monkeyManipulatorFactory, context.copy(), registeredArbitraryBuilders, + namedArbitraryBuilderMap, monkeyContext, + manipulatorOptimizer, instantiatorProcessor ); } @@ -559,7 +612,9 @@ private DefaultArbitraryBuilder generateArbitraryBuilderLazily(LazyArbitr monkeyManipulatorFactory, context, registeredArbitraryBuilders, + namedArbitraryBuilderMap, monkeyContext, + manipulatorOptimizer, instantiatorProcessor ); } 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 ab302fbab..dec1423a3 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 @@ -800,6 +800,76 @@ void registerBuilderGroup() { then(actual3).isEqualTo(ChildBuilderGroup.FIXED_INT_VALUE); } + @Property + void registeredName() { + FixtureMonkey sut = FixtureMonkey.builder() + .registeredName( + "test", + String.class, + monkey -> monkey.giveMeBuilder("test") + ) + .build(); + + SimpleObject actual = sut.giveMeBuilder(SimpleObject.class) + .selectName("test") + .sample(); + + String actual2 = sut.giveMeBuilder(String.class) + .selectName("test") + .sample(); + + then(actual.getStr()).isEqualTo("test"); + then(actual2).isEqualTo("test"); + } + + @Property + void registeredNameWithSameRegisteredName() { + thenThrownBy(() -> FixtureMonkey.builder() + .registeredName( + "test", + String.class, + monkey -> monkey.giveMeBuilder("test") + ) + .registeredName( + "test", + String.class, + monkey -> monkey.giveMeBuilder("test2") + ) + .build() + ).isExactlyInstanceOf(IllegalArgumentException.class) + .hasMessage("Duplicated ArbitraryBuilder name: test"); + } + + @Property + void registeredNameWithUnregisteredName() { + FixtureMonkey sut = FixtureMonkey.builder() + .build(); + + thenThrownBy(() -> sut.giveMeBuilder(SimpleObject.class) + .selectName("test3") + .sample() + ).isExactlyInstanceOf(IllegalArgumentException.class) + .hasMessage("Given name is not registered. name: test3"); + } + + @Property + void generateSampleListWithRegisteredNames() { + FixtureMonkey sut = FixtureMonkey.builder() + .registeredName( + "test", + String.class, + monkey -> monkey.giveMeBuilder("test") + ) + .build(); + + List actual = sut.giveMeBuilder(SimpleObject.class) + .selectName("test") + .sampleList(3); + + then(actual).hasSize(3); + then(actual).allMatch(it -> it.getStr().equals("test")); + } + @Property void registerSameInstancesTwiceWorksLast() { FixtureMonkey sut = FixtureMonkey.builder()