Skip to content

Commit ba9b94b

Browse files
authored
Implement Random Selection of Register Operations When Not Using selectName (#1108)
1 parent e508ba8 commit ba9b94b

File tree

10 files changed

+422
-56
lines changed

10 files changed

+422
-56
lines changed

fixture-monkey-api/src/main/java/com/navercorp/fixturemonkey/api/matcher/MatcherOperator.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@
2323

2424
import com.navercorp.fixturemonkey.api.property.Property;
2525

26-
@API(since = "0.4.0", status = Status.MAINTAINED)
27-
public final class MatcherOperator<T> implements Matcher {
26+
@API(since = "0.4.0", status = Status.INTERNAL)
27+
public class MatcherOperator<T> implements Matcher {
2828
private final Matcher matcher;
2929
private final T operator;
3030

fixture-monkey/src/main/java/com/navercorp/fixturemonkey/FixtureMonkey.java

+51-13
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import static java.util.stream.Collectors.toList;
2222

2323
import java.util.ArrayList;
24+
import java.util.Collections;
25+
import java.util.Comparator;
2426
import java.util.List;
2527
import java.util.Map;
2628
import java.util.function.Function;
@@ -36,10 +38,12 @@
3638
import com.navercorp.fixturemonkey.api.matcher.NamedMatcher;
3739
import com.navercorp.fixturemonkey.api.option.FixtureMonkeyOptions;
3840
import com.navercorp.fixturemonkey.api.property.RootProperty;
41+
import com.navercorp.fixturemonkey.api.random.Randoms;
3942
import com.navercorp.fixturemonkey.api.type.LazyAnnotatedType;
4043
import com.navercorp.fixturemonkey.api.type.TypeReference;
4144
import com.navercorp.fixturemonkey.customizer.ArbitraryManipulator;
4245
import com.navercorp.fixturemonkey.customizer.MonkeyManipulatorFactory;
46+
import com.navercorp.fixturemonkey.customizer.PriorityMatcherOperator;
4347
import com.navercorp.fixturemonkey.experimental.ExperimentalArbitraryBuilder;
4448
import com.navercorp.fixturemonkey.resolver.ArbitraryBuilderContext;
4549
import com.navercorp.fixturemonkey.resolver.ArbitraryResolver;
@@ -53,25 +57,28 @@ public final class FixtureMonkey {
5357
private final ArbitraryTraverser traverser;
5458
private final ManipulatorOptimizer manipulatorOptimizer;
5559
private final MonkeyContext monkeyContext;
56-
private final List<MatcherOperator<? extends ArbitraryBuilder<?>>> registeredArbitraryBuilders = new ArrayList<>();
60+
private final List<PriorityMatcherOperator<? extends ArbitraryBuilder<?>>> registeredArbitraryBuilders
61+
= new ArrayList<>();
5762
private final MonkeyManipulatorFactory monkeyManipulatorFactory;
5863

5964
public FixtureMonkey(
6065
FixtureMonkeyOptions fixtureMonkeyOptions,
6166
ArbitraryTraverser traverser,
6267
ManipulatorOptimizer manipulatorOptimizer,
6368
MonkeyContext monkeyContext,
64-
List<MatcherOperator<Function<FixtureMonkey, ? extends ArbitraryBuilder<?>>>> registeredArbitraryBuilders,
69+
List<PriorityMatcherOperator<Function<FixtureMonkey,
70+
? extends ArbitraryBuilder<?>>>> registeredArbitraryBuildersWithPriority,
6571
MonkeyManipulatorFactory monkeyManipulatorFactory,
66-
Map<String, MatcherOperator<Function<FixtureMonkey, ? extends ArbitraryBuilder<?>>>> mapsByRegisteredName
72+
Map<String, PriorityMatcherOperator<Function<FixtureMonkey,
73+
? extends ArbitraryBuilder<?>>>> registeredPriorityMatchersByName
6774
) {
6875
this.fixtureMonkeyOptions = fixtureMonkeyOptions;
6976
this.traverser = traverser;
7077
this.manipulatorOptimizer = manipulatorOptimizer;
7178
this.monkeyContext = monkeyContext;
7279
this.monkeyManipulatorFactory = monkeyManipulatorFactory;
73-
initializeRegisteredArbitraryBuilders(registeredArbitraryBuilders);
74-
initializeNamedArbitraryBuilderMap(mapsByRegisteredName);
80+
initializeRegisteredArbitraryBuilders(registeredArbitraryBuildersWithPriority);
81+
initializeNamedArbitraryBuilderMap(registeredPriorityMatchersByName);
7582
}
7683

7784
public static FixtureMonkeyBuilder builder() {
@@ -91,8 +98,20 @@ public <T> ArbitraryBuilder<T> giveMeBuilder(Class<T> type) {
9198
public <T> ArbitraryBuilder<T> giveMeBuilder(TypeReference<T> type) {
9299
RootProperty rootProperty = new RootProperty(type.getAnnotatedType());
93100

94-
ArbitraryBuilderContext builderContext = registeredArbitraryBuilders.stream()
101+
List<PriorityMatcherOperator<? extends ArbitraryBuilder<?>>> priorityOperators = registeredArbitraryBuilders
102+
.stream()
95103
.filter(it -> it.match(rootProperty))
104+
.sorted(Comparator.comparingInt(PriorityMatcherOperator::getPriority))
105+
.collect(toList());
106+
107+
List<PriorityMatcherOperator<? extends ArbitraryBuilder<?>>> highestPriorityOperators
108+
= getHighestPriorityOperators(priorityOperators);
109+
110+
if (highestPriorityOperators.size() > 1) {
111+
Collections.shuffle(highestPriorityOperators, Randoms.current());
112+
}
113+
114+
ArbitraryBuilderContext builderContext = highestPriorityOperators.stream()
96115
.map(MatcherOperator::getOperator)
97116
.findAny()
98117
.map(DefaultArbitraryBuilder.class::cast)
@@ -120,6 +139,20 @@ public <T> ArbitraryBuilder<T> giveMeBuilder(TypeReference<T> type) {
120139
);
121140
}
122141

142+
private List<PriorityMatcherOperator<? extends ArbitraryBuilder<?>>> getHighestPriorityOperators(
143+
List<PriorityMatcherOperator<? extends ArbitraryBuilder<?>>> priorityOperators
144+
) {
145+
if (priorityOperators.isEmpty()) {
146+
return priorityOperators;
147+
}
148+
149+
int highestPriority = priorityOperators.get(0).getPriority();
150+
151+
return priorityOperators.stream()
152+
.filter(it -> it.getPriority() == highestPriority)
153+
.collect(toList());
154+
}
155+
123156
public <T> ArbitraryBuilder<T> giveMeBuilder(T value) {
124157
ArbitraryBuilderContext context = new ArbitraryBuilderContext();
125158

@@ -189,11 +222,14 @@ public <T> Arbitrary<T> giveMeArbitrary(TypeReference<T> typeReference) {
189222
}
190223

191224
private void initializeRegisteredArbitraryBuilders(
192-
List<MatcherOperator<Function<FixtureMonkey, ? extends ArbitraryBuilder<?>>>> registeredArbitraryBuilders
225+
List<PriorityMatcherOperator<Function<FixtureMonkey,
226+
? extends ArbitraryBuilder<?>>>> registeredArbitraryBuildersWithPriority
193227
) {
194-
List<? extends MatcherOperator<? extends ArbitraryBuilder<?>>> generatedRegisteredArbitraryBuilder =
195-
registeredArbitraryBuilders.stream()
196-
.map(it -> new MatcherOperator<>(it.getMatcher(), it.getOperator().apply(this)))
228+
List<? extends PriorityMatcherOperator<? extends ArbitraryBuilder<?>>> generatedRegisteredArbitraryBuilder =
229+
registeredArbitraryBuildersWithPriority.stream()
230+
.map(it -> new PriorityMatcherOperator<>(
231+
it.getMatcher(), it.getOperator().apply(this), it.getPriority())
232+
)
197233
.collect(toList());
198234

199235
for (int i = generatedRegisteredArbitraryBuilder.size() - 1; i >= 0; i--) {
@@ -202,13 +238,15 @@ private void initializeRegisteredArbitraryBuilders(
202238
}
203239

204240
private void initializeNamedArbitraryBuilderMap(
205-
Map<String, MatcherOperator<Function<FixtureMonkey, ? extends ArbitraryBuilder<?>>>> mapsByRegisteredName
241+
Map<String, PriorityMatcherOperator<Function<FixtureMonkey,
242+
? extends ArbitraryBuilder<?>>>> mapsByRegisteredName
206243
) {
207244
mapsByRegisteredName.forEach((registeredName, matcherOperator) -> {
208245
registeredArbitraryBuilders.add(
209-
new MatcherOperator<>(
246+
new PriorityMatcherOperator<>(
210247
new NamedMatcher(matcherOperator.getMatcher(), registeredName),
211-
matcherOperator.getOperator().apply(this)
248+
matcherOperator.getOperator().apply(this),
249+
matcherOperator.getPriority()
212250
)
213251
);
214252
});

fixture-monkey/src/main/java/com/navercorp/fixturemonkey/FixtureMonkeyBuilder.java

+71-11
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.apiguardian.api.API;
3333
import org.apiguardian.api.API.Status;
3434

35+
import com.navercorp.fixturemonkey.annotation.Order;
3536
import com.navercorp.fixturemonkey.api.constraint.JavaConstraintGenerator;
3637
import com.navercorp.fixturemonkey.api.container.DecomposedContainerValueFactory;
3738
import com.navercorp.fixturemonkey.api.context.MonkeyContext;
@@ -62,6 +63,7 @@
6263
import com.navercorp.fixturemonkey.buildergroup.ArbitraryBuilderCandidate;
6364
import com.navercorp.fixturemonkey.buildergroup.ArbitraryBuilderGroup;
6465
import com.navercorp.fixturemonkey.customizer.MonkeyManipulatorFactory;
66+
import com.navercorp.fixturemonkey.customizer.PriorityMatcherOperator;
6567
import com.navercorp.fixturemonkey.expression.ArbitraryExpressionFactory;
6668
import com.navercorp.fixturemonkey.expression.MonkeyExpressionFactory;
6769
import com.navercorp.fixturemonkey.resolver.ManipulatorOptimizer;
@@ -72,14 +74,16 @@
7274
@SuppressWarnings("unused")
7375
@API(since = "0.4.0", status = Status.MAINTAINED)
7476
public final class FixtureMonkeyBuilder {
77+
private static final int DEFAULT_PRIORITY = Integer.MAX_VALUE;
78+
7579
private final FixtureMonkeyOptionsBuilder fixtureMonkeyOptionsBuilder = FixtureMonkeyOptions.builder();
76-
private final List<MatcherOperator<Function<FixtureMonkey, ? extends ArbitraryBuilder<?>>>>
77-
registeredArbitraryBuilders = new ArrayList<>();
80+
private final List<PriorityMatcherOperator<Function<FixtureMonkey, ? extends ArbitraryBuilder<?>>>>
81+
registeredArbitraryBuildersWithPriority = new ArrayList<>();
7882
private ManipulatorOptimizer manipulatorOptimizer = new NoneManipulatorOptimizer();
7983
private MonkeyExpressionFactory monkeyExpressionFactory = new ArbitraryExpressionFactory();
8084
private final MonkeyContextBuilder monkeyContextBuilder = MonkeyContext.builder();
81-
private final Map<String, MatcherOperator<Function<FixtureMonkey, ? extends ArbitraryBuilder<?>>>>
82-
registeredArbitraryListByRegisteredName = new HashMap<>();
85+
private final Map<String, PriorityMatcherOperator<Function<FixtureMonkey, ? extends ArbitraryBuilder<?>>>>
86+
registeredPriorityMatchersByName = new HashMap<>();
8387
private long seed = System.nanoTime();
8488

8589
// The default plugins are listed below.
@@ -310,11 +314,37 @@ public FixtureMonkeyBuilder addExceptGeneratePackages(String... exceptGeneratePa
310314
return this;
311315
}
312316

317+
/**
318+
* Registers an ArbitraryBuilder with the DEFAULT priority (Integer.MAX_VALUE).
319+
*
320+
* @param registeredArbitraryBuilder the MatcherOperator containing the matcher
321+
* and the ArbitraryBuilder to be registered
322+
* @return the current instance of FixtureMonkeyBuilder for method chaining
323+
*/
313324
public FixtureMonkeyBuilder register(
314325
Class<?> type,
315326
Function<FixtureMonkey, ? extends ArbitraryBuilder<?>> registeredArbitraryBuilder
316327
) {
317-
return this.register(MatcherOperator.assignableTypeMatchOperator(type, registeredArbitraryBuilder));
328+
return this.register(type, registeredArbitraryBuilder, DEFAULT_PRIORITY);
329+
}
330+
331+
/**
332+
* Registers an ArbitraryBuilder with a specified priority.
333+
*
334+
* @param registeredArbitraryBuilder the MatcherOperator containing the matcher
335+
* and the ArbitraryBuilder to be registered
336+
* @param priority the priority of the ArbitraryBuilder; higher values indicate lower priority
337+
* @return the current instance of FixtureMonkeyBuilder for method chaining
338+
* @throws IllegalArgumentException if the priority is less than 0
339+
*
340+
* If multiple ArbitraryBuilders have the same priority, one of them will be selected randomly.
341+
*/
342+
public FixtureMonkeyBuilder register(
343+
Class<?> type,
344+
Function<FixtureMonkey, ? extends ArbitraryBuilder<?>> registeredArbitraryBuilder,
345+
int priority
346+
) {
347+
return this.register(MatcherOperator.assignableTypeMatchOperator(type, registeredArbitraryBuilder), priority);
318348
}
319349

320350
public FixtureMonkeyBuilder registerExactType(
@@ -334,7 +364,18 @@ public FixtureMonkeyBuilder registerAssignableType(
334364
public FixtureMonkeyBuilder register(
335365
MatcherOperator<Function<FixtureMonkey, ? extends ArbitraryBuilder<?>>> registeredArbitraryBuilder
336366
) {
337-
this.registeredArbitraryBuilders.add(registeredArbitraryBuilder);
367+
return this.register(registeredArbitraryBuilder, DEFAULT_PRIORITY);
368+
}
369+
370+
public FixtureMonkeyBuilder register(
371+
MatcherOperator<Function<FixtureMonkey, ? extends ArbitraryBuilder<?>>> registeredArbitraryBuilder,
372+
int priority
373+
) {
374+
this.registeredArbitraryBuildersWithPriority.add(
375+
new PriorityMatcherOperator<>(
376+
registeredArbitraryBuilder.getMatcher(), registeredArbitraryBuilder.getOperator(), priority
377+
)
378+
);
338379
return this;
339380
}
340381

@@ -361,6 +402,11 @@ public FixtureMonkeyBuilder registerGroup(Class<?>... arbitraryBuilderGroups) {
361402
throw new RuntimeException(ex);
362403
}
363404
};
405+
406+
if (arbitraryBuilderGroup.isAnnotationPresent(Order.class)) {
407+
Order order = arbitraryBuilderGroup.getAnnotation(Order.class);
408+
this.register(actualType, registerArbitraryBuilder, order.value());
409+
}
364410
this.register(actualType, registerArbitraryBuilder);
365411
} catch (Exception ex) {
366412
// ignored
@@ -378,7 +424,8 @@ public FixtureMonkeyBuilder registerGroup(ArbitraryBuilderGroup... arbitraryBuil
378424
for (ArbitraryBuilderCandidate<?> candidate : candidates) {
379425
this.register(
380426
candidate.getClassType(),
381-
candidate.getArbitraryBuilderRegisterer()
427+
candidate.getArbitraryBuilderRegisterer(),
428+
DEFAULT_PRIORITY
382429
);
383430
}
384431
}
@@ -390,13 +437,26 @@ public FixtureMonkeyBuilder registeredName(
390437
Class<?> type,
391438
Function<FixtureMonkey, ? extends ArbitraryBuilder<?>> arbitraryBuilder
392439
) {
393-
if (registeredArbitraryListByRegisteredName.containsKey(registeredName)) {
440+
return this.registeredName(registeredName, type, arbitraryBuilder, DEFAULT_PRIORITY);
441+
}
442+
443+
public FixtureMonkeyBuilder registeredName(
444+
String registeredName,
445+
Class<?> type,
446+
Function<FixtureMonkey, ? extends ArbitraryBuilder<?>> arbitraryBuilder,
447+
int priority
448+
) {
449+
if (registeredPriorityMatchersByName.containsKey(registeredName)) {
394450
throw new IllegalArgumentException("Duplicated ArbitraryBuilder name: " + registeredName);
395451
}
396452
MatcherOperator<Function<FixtureMonkey, ? extends ArbitraryBuilder<?>>> matcherOperator =
397453
MatcherOperator.assignableTypeMatchOperator(type, arbitraryBuilder);
398454

399-
this.registeredArbitraryListByRegisteredName.put(registeredName, matcherOperator);
455+
this.registeredPriorityMatchersByName.put(
456+
registeredName, new PriorityMatcherOperator<>(
457+
matcherOperator.getMatcher(), matcherOperator.getOperator(), priority
458+
)
459+
);
400460
return this;
401461
}
402462

@@ -570,9 +630,9 @@ public FixtureMonkey build() {
570630
traverser,
571631
manipulatorOptimizer,
572632
monkeyContext,
573-
registeredArbitraryBuilders,
633+
registeredArbitraryBuildersWithPriority,
574634
monkeyManipulatorFactory,
575-
registeredArbitraryListByRegisteredName
635+
registeredPriorityMatchersByName
576636
);
577637
}
578638
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Fixture Monkey
3+
*
4+
* Copyright (c) 2021-present NAVER Corp.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
package com.navercorp.fixturemonkey.annotation;
20+
21+
import static org.apiguardian.api.API.Status.EXPERIMENTAL;
22+
23+
import java.lang.annotation.ElementType;
24+
import java.lang.annotation.Retention;
25+
import java.lang.annotation.RetentionPolicy;
26+
import java.lang.annotation.Target;
27+
28+
import org.apiguardian.api.API;
29+
30+
@Retention(RetentionPolicy.RUNTIME)
31+
@Target({ElementType.TYPE})
32+
@API(since = "1.1.10", status = EXPERIMENTAL)
33+
public @interface Order {
34+
int value() default 0;
35+
}

0 commit comments

Comments
 (0)