From b2abc78dee175080ea304e6c3a5861657dee379b Mon Sep 17 00:00:00 2001 From: yongjunhong Date: Fri, 29 Nov 2024 16:57:48 +0900 Subject: [PATCH 1/7] Implement random selection Signed-off-by: yongjunhong --- .../com/navercorp/fixturemonkey/FixtureMonkey.java | 7 +++++++ .../resolver/DefaultArbitraryBuilder.java | 1 + .../test/FixtureMonkeyOptionsTest.java | 13 ------------- 3 files changed, 8 insertions(+), 13 deletions(-) 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..f97d7df76 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,7 @@ import static java.util.stream.Collectors.toList; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.function.Function; @@ -36,6 +37,7 @@ 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; @@ -72,6 +74,7 @@ public FixtureMonkey( this.monkeyManipulatorFactory = monkeyManipulatorFactory; initializeRegisteredArbitraryBuilders(registeredArbitraryBuilders); initializeNamedArbitraryBuilderMap(mapsByRegisteredName); + shuffleRegisteredArbitraryBuilders(); } public static FixtureMonkeyBuilder builder() { @@ -213,4 +216,8 @@ private void initializeNamedArbitraryBuilderMap( ); }); } + + private void shuffleRegisteredArbitraryBuilders() { + Collections.shuffle(registeredArbitraryBuilders, Randoms.current()); + } } 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..56f3a6661 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 @@ -183,6 +183,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/FixtureMonkeyOptionsTest.java b/fixture-monkey/src/test/java/com/navercorp/fixturemonkey/test/FixtureMonkeyOptionsTest.java index da569add4..04259b5b4 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 @@ -858,19 +858,6 @@ void generateSampleListWithRegisteredNames() { then(actual).allMatch(it -> it.getStr().equals("test")); } - @Property - void registerSameInstancesTwiceWorksLast() { - 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(); - - then(actual).isEqualTo("test2"); - } - @Property void registerSetFirst() { String expected = "test2"; From 6e64284577e10ce203ad7472efb813de3a44a92d Mon Sep 17 00:00:00 2001 From: yongjunhong Date: Thu, 5 Dec 2024 14:16:31 +0900 Subject: [PATCH 2/7] Add priority feature Signed-off-by: yongjunhong --- .../fixturemonkey/FixtureMonkey.java | 79 +++++++++++------- .../fixturemonkey/FixtureMonkeyBuilder.java | 68 +++++++++++++-- .../fixturemonkey/annotation/Order.java | 30 +++++++ .../customizer/PriorityMatcherOperator.java | 60 ++++++++++++++ ...xtureMonkeyOptionsAdditionalTestSpecs.java | 61 ++++++++++++++ .../test/FixtureMonkeyOptionsTest.java | 83 ++++++++++++++++++- 6 files changed, 346 insertions(+), 35 deletions(-) create mode 100644 fixture-monkey/src/main/java/com/navercorp/fixturemonkey/annotation/Order.java create mode 100644 fixture-monkey/src/main/java/com/navercorp/fixturemonkey/customizer/PriorityMatcherOperator.java 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 f97d7df76..aa2b41228 100644 --- a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/FixtureMonkey.java +++ b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/FixtureMonkey.java @@ -22,9 +22,11 @@ import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.function.Function; +import java.util.TreeMap; +import java.util.stream.Collectors; import java.util.stream.Stream; import org.apiguardian.api.API; @@ -42,6 +44,7 @@ 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; @@ -57,24 +60,26 @@ public final class FixtureMonkey { private final MonkeyContext monkeyContext; private final List>> registeredArbitraryBuilders = new ArrayList<>(); private final MonkeyManipulatorFactory monkeyManipulatorFactory; + private final Map>>> + priorityGroupedMatchers = new HashMap<>(); public FixtureMonkey( FixtureMonkeyOptions fixtureMonkeyOptions, ArbitraryTraverser traverser, ManipulatorOptimizer manipulatorOptimizer, MonkeyContext monkeyContext, - List>>> registeredArbitraryBuilders, + List registeredArbitraryBuilders, MonkeyManipulatorFactory monkeyManipulatorFactory, - Map>>> mapsByRegisteredName + Map mapsByRegisteredName ) { this.fixtureMonkeyOptions = fixtureMonkeyOptions; this.traverser = traverser; this.manipulatorOptimizer = manipulatorOptimizer; this.monkeyContext = monkeyContext; this.monkeyManipulatorFactory = monkeyManipulatorFactory; - initializeRegisteredArbitraryBuilders(registeredArbitraryBuilders); - initializeNamedArbitraryBuilderMap(mapsByRegisteredName); - shuffleRegisteredArbitraryBuilders(); + groupMatchersByPriorityFromList(registeredArbitraryBuilders); + groupMatchersByPriorityFromNamedMap(mapsByRegisteredName); + shuffleAndRegisterByPriority(priorityGroupedMatchers); } public static FixtureMonkeyBuilder builder() { @@ -191,33 +196,51 @@ public Arbitrary giveMeArbitrary(TypeReference typeReference) { return this.giveMeBuilder(typeReference).build(); } - private void initializeRegisteredArbitraryBuilders( - List>>> registeredArbitraryBuilders + private void groupMatchersByPriorityFromList( + List registeredArbitraryBuilders ) { - List>> generatedRegisteredArbitraryBuilder = - registeredArbitraryBuilders.stream() - .map(it -> new MatcherOperator<>(it.getMatcher(), it.getOperator().apply(this))) - .collect(toList()); - - for (int i = generatedRegisteredArbitraryBuilder.size() - 1; i >= 0; i--) { - this.registeredArbitraryBuilders.add(generatedRegisteredArbitraryBuilder.get(i)); - } + priorityGroupedMatchers.putAll( + registeredArbitraryBuilders + .stream() + .collect(Collectors.groupingBy( + PriorityMatcherOperator::getPriority, + Collectors.mapping( + it -> new MatcherOperator<>( + it.getMatcherOperator(), it.getMatcherOperator().getOperator().apply(this) + ), + Collectors.toList() + ) + )) + ); } - private void initializeNamedArbitraryBuilderMap( - Map>>> mapsByRegisteredName + private void groupMatchersByPriorityFromNamedMap( + Map mapsByRegisteredName ) { - mapsByRegisteredName.forEach((registeredName, matcherOperator) -> { - registeredArbitraryBuilders.add( - new MatcherOperator<>( - new NamedMatcher(matcherOperator.getMatcher(), registeredName), - matcherOperator.getOperator().apply(this) - ) - ); - }); + priorityGroupedMatchers.putAll( + mapsByRegisteredName.entrySet().stream() + .collect(Collectors.groupingBy( + entry -> entry.getValue().getPriority(), + Collectors.mapping( + entry -> new MatcherOperator<>( + new NamedMatcher(entry.getValue().getMatcherOperator().getMatcher(), entry.getKey()), + entry.getValue().getMatcherOperator().getOperator().apply(this) + ), + Collectors.toList() + ) + )) + ); } - private void shuffleRegisteredArbitraryBuilders() { - Collections.shuffle(registeredArbitraryBuilders, Randoms.current()); + private void shuffleAndRegisterByPriority( + Map>>> groupedByPriority + ) { + TreeMap>>> sortedGroupedByPriority = + new TreeMap<>(groupedByPriority); + + sortedGroupedByPriority.forEach((priority, matcherOperators) -> { + Collections.shuffle(matcherOperators, Randoms.current()); + this.registeredArbitraryBuilders.addAll(matcherOperators); + }); } } 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..4d96aabce 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; @@ -73,13 +75,11 @@ @API(since = "0.4.0", status = Status.MAINTAINED) public final class FixtureMonkeyBuilder { private final FixtureMonkeyOptionsBuilder fixtureMonkeyOptionsBuilder = FixtureMonkeyOptions.builder(); - private final List>>> - registeredArbitraryBuilders = new ArrayList<>(); + private final List registeredArbitraryBuilders = 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 registeredArbitraryListByRegisteredName = new HashMap<>(); private long seed = System.nanoTime(); // The default plugins are listed below. @@ -317,6 +317,14 @@ public FixtureMonkeyBuilder register( return this.register(MatcherOperator.assignableTypeMatchOperator(type, registeredArbitraryBuilder)); } + public FixtureMonkeyBuilder register( + Class type, + Function> registeredArbitraryBuilder, + int priority + ) { + return this.register(MatcherOperator.assignableTypeMatchOperator(type, registeredArbitraryBuilder), priority); + } + public FixtureMonkeyBuilder registerExactType( Class type, Function> registeredArbitraryBuilder @@ -334,7 +342,15 @@ public FixtureMonkeyBuilder registerAssignableType( public FixtureMonkeyBuilder register( MatcherOperator>> registeredArbitraryBuilder ) { - this.registeredArbitraryBuilders.add(registeredArbitraryBuilder); + this.registeredArbitraryBuilders.add(new PriorityMatcherOperator(registeredArbitraryBuilder)); + return this; + } + + public FixtureMonkeyBuilder register( + MatcherOperator>> registeredArbitraryBuilder, + int priority + ) { + this.registeredArbitraryBuilders.add(new PriorityMatcherOperator(registeredArbitraryBuilder, priority)); return this; } @@ -361,6 +377,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 @@ -385,6 +406,23 @@ public FixtureMonkeyBuilder registerGroup(ArbitraryBuilderGroup... arbitraryBuil 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; + } + public FixtureMonkeyBuilder registeredName( String registeredName, Class type, @@ -396,7 +434,25 @@ public FixtureMonkeyBuilder registeredName( MatcherOperator>> matcherOperator = MatcherOperator.assignableTypeMatchOperator(type, arbitraryBuilder); - this.registeredArbitraryListByRegisteredName.put(registeredName, matcherOperator); + this.registeredArbitraryListByRegisteredName.put(registeredName, new PriorityMatcherOperator(matcherOperator)); + return this; + } + + public FixtureMonkeyBuilder registeredName( + String registeredName, + Class type, + Function> arbitraryBuilder, + int priority + ) { + if (registeredArbitraryListByRegisteredName.containsKey(registeredName)) { + throw new IllegalArgumentException("Duplicated ArbitraryBuilder name: " + registeredName); + } + MatcherOperator>> matcherOperator = + MatcherOperator.assignableTypeMatchOperator(type, arbitraryBuilder); + + this.registeredArbitraryListByRegisteredName.put( + registeredName, new PriorityMatcherOperator(matcherOperator, priority) + ); return this; } 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..a8d260658 --- /dev/null +++ b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/annotation/Order.java @@ -0,0 +1,30 @@ +/* + * 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 java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE}) +public @interface Order { + int value() default 0; +} 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..096322399 --- /dev/null +++ b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/customizer/PriorityMatcherOperator.java @@ -0,0 +1,60 @@ +/* + * 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 java.util.function.Function; + +import com.navercorp.fixturemonkey.ArbitraryBuilder; +import com.navercorp.fixturemonkey.FixtureMonkey; +import com.navercorp.fixturemonkey.api.matcher.MatcherOperator; + +public final class PriorityMatcherOperator { + private final int priority; + private final MatcherOperator>> matcherOperator; + + public PriorityMatcherOperator( + MatcherOperator>> matcherOperator, + int priority + ) { + checkPriority(priority); + this.matcherOperator = matcherOperator; + this.priority = priority; + } + + public PriorityMatcherOperator( + final MatcherOperator>> matcherOperator + ) { + this.priority = Integer.MAX_VALUE; + this.matcherOperator = matcherOperator; + } + + public int getPriority() { + return priority; + } + + public MatcherOperator>> getMatcherOperator() { + return matcherOperator; + } + + 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/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 04259b5b4..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 @@ -872,6 +873,86 @@ void registerSetFirst() { then(actual).isEqualTo(expected); } + @Property + void registerWithPriority() { + FixtureMonkey sut = FixtureMonkey.builder() + .register(String.class, monkey -> monkey.giveMeBuilder("test"), 1) + .register(String.class, monkey -> monkey.giveMeBuilder("test2"), 2) + .build(); + + String actual = sut.giveMeBuilder(String.class) + .sample(); + + 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 void nullableElement() { FixtureMonkey sut = FixtureMonkey.builder() From 6fa32d7e9bbfa71e9e0e12794f2f3724c9b7ba66 Mon Sep 17 00:00:00 2001 From: yongjunhong Date: Sat, 7 Dec 2024 01:39:10 +0900 Subject: [PATCH 3/7] Reduce code duplication Signed-off-by: yongjunhong --- .../fixturemonkey/FixtureMonkey.java | 54 +++++++++---------- .../fixturemonkey/FixtureMonkeyBuilder.java | 42 ++++++--------- .../customizer/PriorityMatcherOperator.java | 7 --- 3 files changed, 42 insertions(+), 61 deletions(-) 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 aa2b41228..d1e93129b 100644 --- a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/FixtureMonkey.java +++ b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/FixtureMonkey.java @@ -25,6 +25,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.SortedMap; import java.util.TreeMap; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -60,26 +61,25 @@ public final class FixtureMonkey { private final MonkeyContext monkeyContext; private final List>> registeredArbitraryBuilders = new ArrayList<>(); private final MonkeyManipulatorFactory monkeyManipulatorFactory; - private final Map>>> - priorityGroupedMatchers = new HashMap<>(); public FixtureMonkey( FixtureMonkeyOptions fixtureMonkeyOptions, 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; - groupMatchersByPriorityFromList(registeredArbitraryBuilders); - groupMatchersByPriorityFromNamedMap(mapsByRegisteredName); - shuffleAndRegisterByPriority(priorityGroupedMatchers); + Map>>> registeredGroupMatchersListByPriority = + groupMatchersByPriority(registeredArbitraryBuildersWithPriority); + groupMatchersByPriorityFromNamedMap(registeredPriorityMatchersByName, registeredGroupMatchersListByPriority); + shuffleAndRegisterByPriority(registeredGroupMatchersListByPriority); } public static FixtureMonkeyBuilder builder() { @@ -196,29 +196,29 @@ public Arbitrary giveMeArbitrary(TypeReference typeReference) { return this.giveMeBuilder(typeReference).build(); } - private void groupMatchersByPriorityFromList( - List registeredArbitraryBuilders + private Map>>> groupMatchersByPriority( + List registeredArbitraryBuildersWithPriority ) { - priorityGroupedMatchers.putAll( - registeredArbitraryBuilders - .stream() - .collect(Collectors.groupingBy( - PriorityMatcherOperator::getPriority, - Collectors.mapping( - it -> new MatcherOperator<>( - it.getMatcherOperator(), it.getMatcherOperator().getOperator().apply(this) - ), - Collectors.toList() - ) - )) + return new HashMap<>(registeredArbitraryBuildersWithPriority + .stream() + .collect(Collectors.groupingBy( + PriorityMatcherOperator::getPriority, + Collectors.mapping( + it -> new MatcherOperator<>( + it.getMatcherOperator(), it.getMatcherOperator().getOperator().apply(this) + ), + Collectors.toList() + ) + )) ); } private void groupMatchersByPriorityFromNamedMap( - Map mapsByRegisteredName + Map registeredPriorityMatchersByName, + Map>>> registeredGroupMatchersListByPriority ) { - priorityGroupedMatchers.putAll( - mapsByRegisteredName.entrySet().stream() + registeredGroupMatchersListByPriority.putAll( + registeredPriorityMatchersByName.entrySet().stream() .collect(Collectors.groupingBy( entry -> entry.getValue().getPriority(), Collectors.mapping( @@ -233,10 +233,10 @@ private void groupMatchersByPriorityFromNamedMap( } private void shuffleAndRegisterByPriority( - Map>>> groupedByPriority + Map>>> registeredGroupMatchersListByPriority ) { - TreeMap>>> sortedGroupedByPriority = - new TreeMap<>(groupedByPriority); + SortedMap>>> sortedGroupedByPriority = + new TreeMap<>(registeredGroupMatchersListByPriority); sortedGroupedByPriority.forEach((priority, matcherOperators) -> { Collections.shuffle(matcherOperators, Randoms.current()); 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 4d96aabce..0cdbc7d4f 100644 --- a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/FixtureMonkeyBuilder.java +++ b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/FixtureMonkeyBuilder.java @@ -74,12 +74,14 @@ @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. @@ -314,7 +316,7 @@ public FixtureMonkeyBuilder register( Class type, Function> registeredArbitraryBuilder ) { - return this.register(MatcherOperator.assignableTypeMatchOperator(type, registeredArbitraryBuilder)); + return this.register(type, registeredArbitraryBuilder, DEFAULT_PRIORITY); } public FixtureMonkeyBuilder register( @@ -342,15 +344,16 @@ public FixtureMonkeyBuilder registerAssignableType( public FixtureMonkeyBuilder register( MatcherOperator>> registeredArbitraryBuilder ) { - this.registeredArbitraryBuilders.add(new PriorityMatcherOperator(registeredArbitraryBuilder)); - return this; + return this.register(registeredArbitraryBuilder, DEFAULT_PRIORITY); } public FixtureMonkeyBuilder register( MatcherOperator>> registeredArbitraryBuilder, int priority ) { - this.registeredArbitraryBuilders.add(new PriorityMatcherOperator(registeredArbitraryBuilder, priority)); + this.registeredArbitraryBuildersWithPriority.add( + new PriorityMatcherOperator(registeredArbitraryBuilder, priority) + ); return this; } @@ -393,15 +396,7 @@ 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; } @@ -428,14 +423,7 @@ public FixtureMonkeyBuilder 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, new PriorityMatcherOperator(matcherOperator)); - return this; + return this.registeredName(registeredName, type, arbitraryBuilder, DEFAULT_PRIORITY); } public FixtureMonkeyBuilder registeredName( @@ -444,13 +432,13 @@ public FixtureMonkeyBuilder registeredName( Function> arbitraryBuilder, int priority ) { - if (registeredArbitraryListByRegisteredName.containsKey(registeredName)) { + if (registeredPriorityMatchersByName.containsKey(registeredName)) { throw new IllegalArgumentException("Duplicated ArbitraryBuilder name: " + registeredName); } MatcherOperator>> matcherOperator = MatcherOperator.assignableTypeMatchOperator(type, arbitraryBuilder); - this.registeredArbitraryListByRegisteredName.put( + this.registeredPriorityMatchersByName.put( registeredName, new PriorityMatcherOperator(matcherOperator, priority) ); return this; @@ -626,9 +614,9 @@ public FixtureMonkey build() { traverser, manipulatorOptimizer, monkeyContext, - registeredArbitraryBuilders, + registeredArbitraryBuildersWithPriority, monkeyManipulatorFactory, - registeredArbitraryListByRegisteredName + registeredPriorityMatchersByName ); } } 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 index 096322399..621367df2 100644 --- a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/customizer/PriorityMatcherOperator.java +++ b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/customizer/PriorityMatcherOperator.java @@ -37,13 +37,6 @@ public PriorityMatcherOperator( this.priority = priority; } - public PriorityMatcherOperator( - final MatcherOperator>> matcherOperator - ) { - this.priority = Integer.MAX_VALUE; - this.matcherOperator = matcherOperator; - } - public int getPriority() { return priority; } From 486cf5fb9f1695f7406264190b819012cd3d0b71 Mon Sep 17 00:00:00 2001 From: yongjunhong Date: Thu, 26 Dec 2024 20:24:26 +0900 Subject: [PATCH 4/7] Refactor PriorityMatcherOperator Signed-off-by: yongjunhong --- .../api/matcher/MatcherOperator.java | 2 +- .../fixturemonkey/FixtureMonkey.java | 83 +++++++------------ .../fixturemonkey/FixtureMonkeyBuilder.java | 14 +++- .../customizer/MonkeyManipulatorFactory.java | 2 +- .../customizer/PriorityMatcherOperator.java | 17 ++-- .../resolver/ArbitraryResolver.java | 5 +- .../resolver/DefaultArbitraryBuilder.java | 5 +- 7 files changed, 54 insertions(+), 74 deletions(-) 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 d1e93129b..28fee93ea 100644 --- a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/FixtureMonkey.java +++ b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/FixtureMonkey.java @@ -21,13 +21,9 @@ import static java.util.stream.Collectors.toList; import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.SortedMap; -import java.util.TreeMap; -import java.util.stream.Collectors; +import java.util.function.Function; import java.util.stream.Stream; import org.apiguardian.api.API; @@ -40,7 +36,6 @@ 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; @@ -59,7 +54,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( @@ -67,19 +63,19 @@ public FixtureMonkey( ArbitraryTraverser traverser, ManipulatorOptimizer manipulatorOptimizer, MonkeyContext monkeyContext, - List registeredArbitraryBuildersWithPriority, + List>>> registeredArbitraryBuildersWithPriority, MonkeyManipulatorFactory monkeyManipulatorFactory, - Map registeredPriorityMatchersByName + Map>>> registeredPriorityMatchersByName ) { this.fixtureMonkeyOptions = fixtureMonkeyOptions; this.traverser = traverser; this.manipulatorOptimizer = manipulatorOptimizer; this.monkeyContext = monkeyContext; this.monkeyManipulatorFactory = monkeyManipulatorFactory; - Map>>> registeredGroupMatchersListByPriority = - groupMatchersByPriority(registeredArbitraryBuildersWithPriority); - groupMatchersByPriorityFromNamedMap(registeredPriorityMatchersByName, registeredGroupMatchersListByPriority); - shuffleAndRegisterByPriority(registeredGroupMatchersListByPriority); + initializeRegisteredArbitraryBuilders(registeredArbitraryBuildersWithPriority); + initializeNamedArbitraryBuilderMap(registeredPriorityMatchersByName); } public static FixtureMonkeyBuilder builder() { @@ -196,51 +192,34 @@ public Arbitrary giveMeArbitrary(TypeReference typeReference) { return this.giveMeBuilder(typeReference).build(); } - private Map>>> groupMatchersByPriority( - List registeredArbitraryBuildersWithPriority + private void initializeRegisteredArbitraryBuilders( + List>>> registeredArbitraryBuilders ) { - return new HashMap<>(registeredArbitraryBuildersWithPriority - .stream() - .collect(Collectors.groupingBy( - PriorityMatcherOperator::getPriority, - Collectors.mapping( - it -> new MatcherOperator<>( - it.getMatcherOperator(), it.getMatcherOperator().getOperator().apply(this) - ), - Collectors.toList() + List>> generatedRegisteredArbitraryBuilder = + registeredArbitraryBuilders.stream() + .map(it -> new PriorityMatcherOperator<>( + it.getMatcher(), it.getOperator().apply(this), it.getPriority()) ) - )) - ); - } + .collect(toList()); - private void groupMatchersByPriorityFromNamedMap( - Map registeredPriorityMatchersByName, - Map>>> registeredGroupMatchersListByPriority - ) { - registeredGroupMatchersListByPriority.putAll( - registeredPriorityMatchersByName.entrySet().stream() - .collect(Collectors.groupingBy( - entry -> entry.getValue().getPriority(), - Collectors.mapping( - entry -> new MatcherOperator<>( - new NamedMatcher(entry.getValue().getMatcherOperator().getMatcher(), entry.getKey()), - entry.getValue().getMatcherOperator().getOperator().apply(this) - ), - Collectors.toList() - ) - )) - ); + for (int i = generatedRegisteredArbitraryBuilder.size() - 1; i >= 0; i--) { + this.registeredArbitraryBuilders.add(generatedRegisteredArbitraryBuilder.get(i)); + } } - private void shuffleAndRegisterByPriority( - Map>>> registeredGroupMatchersListByPriority + private void initializeNamedArbitraryBuilderMap( + Map>>> mapsByRegisteredName ) { - SortedMap>>> sortedGroupedByPriority = - new TreeMap<>(registeredGroupMatchersListByPriority); - - sortedGroupedByPriority.forEach((priority, matcherOperators) -> { - Collections.shuffle(matcherOperators, Randoms.current()); - this.registeredArbitraryBuilders.addAll(matcherOperators); + mapsByRegisteredName.forEach((registeredName, matcherOperator) -> { + registeredArbitraryBuilders.add( + new PriorityMatcherOperator<>( + new NamedMatcher(matcherOperator.getMatcher(), registeredName), + 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 0cdbc7d4f..c0a5605fd 100644 --- a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/FixtureMonkeyBuilder.java +++ b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/FixtureMonkeyBuilder.java @@ -77,11 +77,13 @@ public final class FixtureMonkeyBuilder { private static final int DEFAULT_PRIORITY = Integer.MAX_VALUE; private final FixtureMonkeyOptionsBuilder fixtureMonkeyOptionsBuilder = FixtureMonkeyOptions.builder(); - private final List registeredArbitraryBuildersWithPriority = 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 registeredPriorityMatchersByName = new HashMap<>(); + private final Map>>> + registeredPriorityMatchersByName = new HashMap<>(); private long seed = System.nanoTime(); // The default plugins are listed below. @@ -352,7 +354,9 @@ public FixtureMonkeyBuilder register( int priority ) { this.registeredArbitraryBuildersWithPriority.add( - new PriorityMatcherOperator(registeredArbitraryBuilder, priority) + new PriorityMatcherOperator<>( + registeredArbitraryBuilder.getMatcher(), registeredArbitraryBuilder.getOperator(), priority + ) ); return this; } @@ -439,7 +443,9 @@ public FixtureMonkeyBuilder registeredName( MatcherOperator.assignableTypeMatchOperator(type, arbitraryBuilder); this.registeredPriorityMatchersByName.put( - registeredName, new PriorityMatcherOperator(matcherOperator, priority) + registeredName, new PriorityMatcherOperator<>( + matcherOperator.getMatcher(), matcherOperator.getOperator(), priority + ) ); return this; } 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..3ab61cb00 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 @@ -142,7 +142,7 @@ public ContainerInfoManipulator newContainerInfoManipulator( } public List newRegisteredArbitraryManipulators( - List>> registeredArbitraryBuilders, + List>> registeredArbitraryBuilders, Map> nodesByType, ArbitraryBuilderContext builderContext ) { 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 index 621367df2..c85bfa5f5 100644 --- a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/customizer/PriorityMatcherOperator.java +++ b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/customizer/PriorityMatcherOperator.java @@ -18,22 +18,19 @@ package com.navercorp.fixturemonkey.customizer; -import java.util.function.Function; - -import com.navercorp.fixturemonkey.ArbitraryBuilder; -import com.navercorp.fixturemonkey.FixtureMonkey; +import com.navercorp.fixturemonkey.api.matcher.Matcher; import com.navercorp.fixturemonkey.api.matcher.MatcherOperator; -public final class PriorityMatcherOperator { +public final class PriorityMatcherOperator extends MatcherOperator { private final int priority; - private final MatcherOperator>> matcherOperator; public PriorityMatcherOperator( - MatcherOperator>> matcherOperator, + Matcher matcher, + T operator, int priority ) { + super(matcher, operator); checkPriority(priority); - this.matcherOperator = matcherOperator; this.priority = priority; } @@ -41,10 +38,6 @@ public int getPriority() { return priority; } - public MatcherOperator>> getMatcherOperator() { - return matcherOperator; - } - 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 56f3a6661..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 From d00d7a7be7bc8919ff4259c70194386db288487d Mon Sep 17 00:00:00 2001 From: yongjunhong Date: Tue, 21 Jan 2025 23:09:54 +0900 Subject: [PATCH 5/7] Add sorting mechanism for initialized instances Signed-off-by: yongjunhong --- .../fixturemonkey/FixtureMonkey.java | 35 ++++++++- .../customizer/MonkeyManipulatorFactory.java | 76 +++++++++++++++---- 2 files changed, 94 insertions(+), 17 deletions(-) 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 28fee93ea..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,6 +38,7 @@ 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; @@ -95,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) @@ -124,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(); @@ -194,10 +223,10 @@ public Arbitrary giveMeArbitrary(TypeReference typeReference) { private void initializeRegisteredArbitraryBuilders( List>>> registeredArbitraryBuilders + ? extends ArbitraryBuilder>>> registeredArbitraryBuildersWithPriority ) { List>> generatedRegisteredArbitraryBuilder = - registeredArbitraryBuilders.stream() + registeredArbitraryBuildersWithPriority.stream() .map(it -> new PriorityMatcherOperator<>( it.getMatcher(), it.getOperator().apply(this), it.getPriority()) ) 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 3ab61cb00..f554a94b7 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 @@ -20,6 +20,7 @@ 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 +45,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; @@ -152,20 +154,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 +172,63 @@ 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()); + + return getHighestPriorityOperator(priorityOperators); + } + + private DefaultArbitraryBuilder getHighestPriorityOperator( + List>> priorityOperators + ) { + if (priorityOperators.isEmpty()) { + return null; + } + + int highestPriority = priorityOperators.get(0).getPriority(); + List>> highestPriorityOperators = priorityOperators + .stream() + .filter(it -> it.getPriority() == highestPriority) + .collect(Collectors.toList()); + + if (highestPriorityOperators.size() > 1) { + Collections.shuffle(highestPriorityOperators, Randoms.current()); + } + + return (DefaultArbitraryBuilder)highestPriorityOperators.stream() + .findFirst() + .map(MatcherOperator::getOperator) + .filter(it -> it instanceof DefaultArbitraryBuilder) + .orElse(null); + } + public ManipulatorSet newManipulatorSet(ManipulatorHolderSet manipulatorHolderSet) { int baseSequence = sequence.getAndIncrement(); From 36c424a4616694d2d105f9fc05a07c31c2ce61d6 Mon Sep 17 00:00:00 2001 From: yongjunhong Date: Thu, 6 Feb 2025 18:57:18 +0900 Subject: [PATCH 6/7] Add javadoc Signed-off-by: yongjunhong --- .../fixturemonkey/FixtureMonkeyBuilder.java | 18 +++++++++++++++ .../customizer/PriorityMatcherOperator.java | 23 +++++++++++++++++++ 2 files changed, 41 insertions(+) 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 c0a5605fd..ec67952d0 100644 --- a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/FixtureMonkeyBuilder.java +++ b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/FixtureMonkeyBuilder.java @@ -314,6 +314,13 @@ 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 @@ -321,6 +328,17 @@ public FixtureMonkeyBuilder register( 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, 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 index c85bfa5f5..aa69cd953 100644 --- a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/customizer/PriorityMatcherOperator.java +++ b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/customizer/PriorityMatcherOperator.java @@ -18,9 +18,21 @@ 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; @@ -34,10 +46,21 @@ public PriorityMatcherOperator( 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"); From 43e59aaf0665082b8cdfbb4a79aa1fa9e77f9a73 Mon Sep 17 00:00:00 2001 From: yongjunhong Date: Thu, 6 Feb 2025 19:11:40 +0900 Subject: [PATCH 7/7] Apply comment Signed-off-by: yongjunhong --- .../fixturemonkey/annotation/Order.java | 5 +++ .../customizer/MonkeyManipulatorFactory.java | 33 ++++++++++--------- 2 files changed, 23 insertions(+), 15 deletions(-) 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 index a8d260658..90e34898e 100644 --- a/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/annotation/Order.java +++ b/fixture-monkey/src/main/java/com/navercorp/fixturemonkey/annotation/Order.java @@ -18,13 +18,18 @@ 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 f554a94b7..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,6 +18,8 @@ package com.navercorp.fixturemonkey.customizer; +import static java.util.stream.Collectors.toList; + import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -202,21 +204,8 @@ private DefaultArbitraryBuilder findRegisteredArbitraryBuilder( .sorted(Comparator.comparingInt(PriorityMatcherOperator::getPriority)) .collect(Collectors.toList()); - return getHighestPriorityOperator(priorityOperators); - } - - private DefaultArbitraryBuilder getHighestPriorityOperator( - List>> priorityOperators - ) { - if (priorityOperators.isEmpty()) { - return null; - } - - int highestPriority = priorityOperators.get(0).getPriority(); - List>> highestPriorityOperators = priorityOperators - .stream() - .filter(it -> it.getPriority() == highestPriority) - .collect(Collectors.toList()); + List>> highestPriorityOperators + = getHighestPriorityOperators(priorityOperators); if (highestPriorityOperators.size() > 1) { Collections.shuffle(highestPriorityOperators, Randoms.current()); @@ -229,6 +218,20 @@ private DefaultArbitraryBuilder getHighestPriorityOperator( .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();