Skip to content

Commit 24a731e

Browse files
authored
Merge pull request quarkusio#49992 from mkouba/mockable-event
Tests: support InjectMock for built-in Event
2 parents 1c5ad99 + 344a91a commit 24a731e

File tree

21 files changed

+466
-62
lines changed

21 files changed

+466
-62
lines changed

extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/ArcProcessor.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -621,11 +621,12 @@ public void registerSubclass(DotName beanClassName, String subclassName) {
621621
@Consume(ResourcesGeneratedPhaseBuildItem.class)
622622
@Record(STATIC_INIT)
623623
public ArcContainerBuildItem initializeContainer(ArcConfig config, ArcRecorder recorder,
624-
ShutdownContextBuildItem shutdown, Optional<CurrentContextFactoryBuildItem> currentContextFactory)
624+
ShutdownContextBuildItem shutdown, Optional<CurrentContextFactoryBuildItem> currentContextFactory,
625+
LaunchModeBuildItem launchMode)
625626
throws Exception {
626627
ArcContainer container = recorder.initContainer(shutdown,
627628
currentContextFactory.isPresent() ? currentContextFactory.get().getFactory() : null,
628-
config.strictCompatibility());
629+
config.strictCompatibility(), launchMode.isTest());
629630
return new ArcContainerBuildItem(container);
630631
}
631632

extensions/arc/runtime/src/main/java/io/quarkus/arc/runtime/ArcRecorder.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,11 @@ public class ArcRecorder {
4545
public static volatile Map<String, Supplier<ActiveResult>> syntheticBeanCheckActive;
4646

4747
public ArcContainer initContainer(ShutdownContext shutdown, RuntimeValue<CurrentContextFactory> currentContextFactory,
48-
boolean strictCompatibility) throws Exception {
49-
ArcInitConfig.Builder builder = ArcInitConfig.builder();
50-
builder.setCurrentContextFactory(currentContextFactory != null ? currentContextFactory.getValue() : null);
51-
builder.setStrictCompatibility(strictCompatibility);
48+
boolean strictCompatibility, boolean testMode) throws Exception {
49+
ArcInitConfig.Builder builder = ArcInitConfig.builder()
50+
.setCurrentContextFactory(currentContextFactory != null ? currentContextFactory.getValue() : null)
51+
.setStrictCompatibility(strictCompatibility)
52+
.setTestMode(testMode);
5253
ArcContainer container = Arc.initialize(builder.build());
5354
shutdown.addShutdownTask(new Runnable() {
5455
@Override

independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/InjectionPointInfo.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ static List<InjectionPointInfo> fromMethod(MethodInfo method, ClassInfo beanClas
106106
return injectionPoints;
107107
}
108108

109-
static InjectionPointInfo fromSyntheticInjectionPoint(TypeAndQualifiers typeAndQualifiers) {
109+
public static InjectionPointInfo fromSyntheticInjectionPoint(TypeAndQualifiers typeAndQualifiers) {
110110
return new InjectionPointInfo(typeAndQualifiers, InjectionPointKind.CDI, null, null, false, false);
111111
}
112112

independent-projects/arc/runtime/src/main/java/io/quarkus/arc/Arc.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ public static ArcContainer initialize(ArcInitConfig config) {
3535
container = INSTANCE.get();
3636
if (container == null) {
3737
// Set the container instance first because Arc.container() can be used within ArcContainerImpl.init()
38-
container = new ArcContainerImpl(config.getCurrentContextFactory(), config.isStrictCompatibility());
38+
container = new ArcContainerImpl(config.getCurrentContextFactory(), config.isStrictCompatibility(),
39+
config.isTestMode());
3940
INSTANCE.set(container);
4041
container.init();
4142
}

independent-projects/arc/runtime/src/main/java/io/quarkus/arc/ArcInitConfig.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,12 @@ public static Builder builder() {
2424
private ArcInitConfig(Builder builder) {
2525
this.currentContextFactory = builder.currentContextFactory;
2626
this.strictCompatibility = builder.strictCompatibility;
27+
this.testMode = builder.testMode;
2728
}
2829

2930
private final boolean strictCompatibility;
3031
private final CurrentContextFactory currentContextFactory;
32+
private final boolean testMode;
3133

3234
public boolean isStrictCompatibility() {
3335
return strictCompatibility;
@@ -37,14 +39,17 @@ public CurrentContextFactory getCurrentContextFactory() {
3739
return currentContextFactory;
3840
}
3941

42+
public boolean isTestMode() {
43+
return testMode;
44+
}
45+
4046
public static class Builder {
47+
4148
private boolean strictCompatibility;
4249
private CurrentContextFactory currentContextFactory;
50+
private boolean testMode;
4351

4452
private Builder() {
45-
// init all values with their defaults
46-
this.strictCompatibility = false;
47-
this.currentContextFactory = null;
4853
}
4954

5055
public Builder setStrictCompatibility(boolean strictCompatibility) {
@@ -57,6 +62,11 @@ public Builder setCurrentContextFactory(CurrentContextFactory currentContextFact
5762
return this;
5863
}
5964

65+
public Builder setTestMode(boolean testMode) {
66+
this.testMode = testMode;
67+
return this;
68+
}
69+
6070
public ArcInitConfig build() {
6171
return new ArcInitConfig(this);
6272
}

independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/ArcContainerImpl.java

Lines changed: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,13 @@
1919
import java.util.Objects;
2020
import java.util.ServiceLoader;
2121
import java.util.Set;
22+
import java.util.concurrent.ConcurrentHashMap;
23+
import java.util.concurrent.ConcurrentMap;
2224
import java.util.concurrent.ExecutorService;
2325
import java.util.concurrent.ForkJoinPool;
2426
import java.util.concurrent.atomic.AtomicBoolean;
2527
import java.util.concurrent.atomic.AtomicInteger;
28+
import java.util.concurrent.atomic.AtomicReference;
2629
import java.util.function.BiConsumer;
2730
import java.util.function.Consumer;
2831
import java.util.function.Supplier;
@@ -105,7 +108,10 @@ public class ArcContainerImpl implements ArcContainer {
105108

106109
private final boolean strictMode;
107110

108-
public ArcContainerImpl(CurrentContextFactory currentContextFactory, boolean strictMode) {
111+
// An event mock reference is shared by identical injection points
112+
private final ConcurrentMap<TypeAndQualifiers, AtomicReference<Event<?>>> eventMocks;
113+
114+
public ArcContainerImpl(CurrentContextFactory currentContextFactory, boolean strictMode, boolean testMode) {
109115
this.strictMode = strictMode;
110116
id = String.valueOf(ID_GENERATOR.incrementAndGet());
111117
running = new AtomicBoolean(true);
@@ -123,6 +129,7 @@ public ArcContainerImpl(CurrentContextFactory currentContextFactory, boolean str
123129
Supplier<ContextInstances> requestContextInstances = null;
124130
this.currentContextFactory = currentContextFactory == null ? new ThreadLocalCurrentContextFactory()
125131
: currentContextFactory;
132+
this.eventMocks = testMode ? new ConcurrentHashMap<>() : null;
126133

127134
List<Components> components = new ArrayList<>();
128135
for (ComponentsProvider componentsProvider : ServiceLoader.load(ComponentsProvider.class)) {
@@ -331,18 +338,18 @@ public <T> Supplier<InstanceHandle<T>> beanInstanceSupplier(Class<T> type, Annot
331338
if (bean == null) {
332339
return null;
333340
}
341+
InjectionPoint injectionPoint = InjectionPointImpl.of(type, qualifiers);
334342
return new Supplier<InstanceHandle<T>>() {
335343
@Override
336344
public InstanceHandle<T> get() {
337-
return beanInstanceHandle(bean, null);
345+
return beanInstanceHandle(bean, null, injectionPoint, null);
338346
}
339347
};
340348
}
341349

342350
@Override
343351
public <T> InstanceHandle<T> instance(InjectableBean<T> bean) {
344-
Objects.requireNonNull(bean);
345-
return beanInstanceHandle(bean, null);
352+
return beanInstanceHandle(Objects.requireNonNull(bean), null, InjectionPointImpl.EMPTY, null);
346353
}
347354

348355
@Override
@@ -397,7 +404,7 @@ public <T> InstanceHandle<T> instance(String name) {
397404
Set<InjectableBean<?>> resolvedBeans = beansByName.getValue(name);
398405
return resolvedBeans.size() != 1 ? EagerInstanceHandle.unavailable()
399406
: (InstanceHandle<T>) beanInstanceHandle(resolvedBeans.iterator()
400-
.next(), null);
407+
.next(), null, InjectionPointImpl.EMPTY, null);
401408
}
402409

403410
@Override
@@ -480,6 +487,9 @@ public synchronized void shutdown() {
480487
resolved.clear();
481488
running.set(false);
482489
InterceptedStaticMethods.clear();
490+
if (eventMocks != null) {
491+
eventMocks.clear();
492+
}
483493

484494
LOGGER.debugf("ArC DI container shut down");
485495
}
@@ -542,16 +552,16 @@ private static void addBuiltInBeans(List<InjectableBean<?>> beans, Map<String, L
542552
}
543553

544554
private <T> InstanceHandle<T> instanceHandle(Type type, Annotation... qualifiers) {
545-
return beanInstanceHandle(getBean(type, qualifiers), null);
555+
return beanInstanceHandle(getBean(type, qualifiers), null, InjectionPointImpl.of(type, qualifiers), null);
546556
}
547557

548558
static <T> InstanceHandle<T> beanInstanceHandle(InjectableBean<T> bean, CreationalContextImpl<T> parentContext,
549-
boolean resetCurrentInjectionPoint, Consumer<T> destroyLogic) {
550-
return beanInstanceHandle(bean, parentContext, resetCurrentInjectionPoint, destroyLogic, false);
559+
InjectionPoint resetInjectionPoint, Consumer<T> destroyLogic) {
560+
return beanInstanceHandle(bean, parentContext, resetInjectionPoint, destroyLogic, false);
551561
}
552562

553563
static <T> InstanceHandle<T> beanInstanceHandle(InjectableBean<T> bean, CreationalContextImpl<T> parentContext,
554-
boolean resetCurrentInjectionPoint, Consumer<T> destroyLogic, boolean useParentCreationalContextDirectly) {
564+
InjectionPoint resetInjectionPoint, Consumer<T> destroyLogic, boolean useParentCreationalContextDirectly) {
555565
if (bean != null) {
556566
if (parentContext == null && Dependent.class.equals(bean.getScope())) {
557567
parentContext = new CreationalContextImpl<>(null);
@@ -563,14 +573,14 @@ static <T> InstanceHandle<T> beanInstanceHandle(InjectableBean<T> bean, Creation
563573
creationalContext = new CreationalContextImpl<>(bean);
564574
}
565575
InjectionPoint prev = null;
566-
if (resetCurrentInjectionPoint) {
567-
prev = InjectionPointProvider.setCurrent(creationalContext, CurrentInjectionPointProvider.EMPTY);
576+
if (resetInjectionPoint != null) {
577+
prev = InjectionPointProvider.setCurrent(creationalContext, resetInjectionPoint);
568578
}
569579
try {
570580
return new EagerInstanceHandle<>(bean, bean.get(creationalContext), creationalContext, parentContext,
571581
destroyLogic);
572582
} finally {
573-
if (resetCurrentInjectionPoint) {
583+
if (resetInjectionPoint != null) {
574584
InjectionPointProvider.setCurrent(creationalContext, prev);
575585
}
576586
}
@@ -579,10 +589,6 @@ static <T> InstanceHandle<T> beanInstanceHandle(InjectableBean<T> bean, Creation
579589
}
580590
}
581591

582-
static <T> InstanceHandle<T> beanInstanceHandle(InjectableBean<T> bean, CreationalContextImpl<T> parentContext) {
583-
return beanInstanceHandle(bean, parentContext, true, null);
584-
}
585-
586592
@SuppressWarnings("unchecked")
587593
private <T> InjectableBean<T> getBean(Type requiredType, Annotation... qualifiers) {
588594
if (qualifiers == null || qualifiers.length == 0) {
@@ -1042,6 +1048,21 @@ public static ArcContainerImpl instance() {
10421048
return unwrap(Arc.container());
10431049
}
10441050

1051+
<T> EventImpl<T> getEvent(Type eventType, Set<Annotation> eventQualifiers, InjectionPoint ip) {
1052+
if (eventMocks != null) {
1053+
AtomicReference<Event<?>> mock = eventMocks.computeIfAbsent(
1054+
new TypeAndQualifiers(ip.getType(), ip.getQualifiers()),
1055+
ArcContainerImpl::newEventMockReference);
1056+
return new MockableEventImpl<>(eventType, eventQualifiers, ip, mock);
1057+
} else {
1058+
return new EventImpl<>(eventType, eventQualifiers, ip);
1059+
}
1060+
}
1061+
1062+
private static AtomicReference<Event<?>> newEventMockReference(TypeAndQualifiers typeAndQualifiers) {
1063+
return new AtomicReference<>();
1064+
}
1065+
10451066
private static final class Resolvable {
10461067

10471068
private static final Annotation[] ANY_QUALIFIER = { Any.Literal.INSTANCE };
@@ -1096,4 +1117,7 @@ public boolean equals(Object obj) {
10961117
}
10971118

10981119
}
1120+
1121+
private record TypeAndQualifiers(Type requiredType, Set<Annotation> qualifiers) {
1122+
}
10991123
}

independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/BeanManagerImpl.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public Object getReference(Bean<?> bean, Type beanType, CreationalContext<?> ctx
6969
InjectionPoint prev = InjectionPointProvider.setCurrent(ctx, null);
7070
try {
7171
return ArcContainerImpl.beanInstanceHandle((InjectableBean) bean, (CreationalContextImpl) ctx,
72-
false, null, true).get();
72+
null, null, true).get();
7373
} finally {
7474
InjectionPointProvider.setCurrent(ctx, prev);
7575
}
@@ -93,7 +93,7 @@ public Object getInjectableReference(InjectionPoint ij, CreationalContext<?> ctx
9393
InjectionPoint prev = InjectionPointProvider.setCurrent(ctx, ij);
9494
try {
9595
return ArcContainerImpl.beanInstanceHandle(bean, (CreationalContextImpl) ctx,
96-
false, null, true).get();
96+
null, null, true).get();
9797
} finally {
9898
InjectionPointProvider.setCurrent(ctx, prev);
9999
}

independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/CurrentInjectionPointProvider.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import java.lang.annotation.Annotation;
44
import java.lang.reflect.Member;
55
import java.lang.reflect.Type;
6-
import java.util.Collections;
76
import java.util.Set;
87
import java.util.function.Supplier;
98

@@ -18,14 +17,11 @@
1817
*/
1918
public class CurrentInjectionPointProvider<T> implements InjectableReferenceProvider<T> {
2019

21-
static final InjectionPoint EMPTY = new InjectionPointImpl(Object.class, Object.class, Collections.emptySet(), null, null,
22-
null, -1, false);
23-
2420
static final Supplier<InjectionPoint> EMPTY_SUPPLIER = new Supplier<InjectionPoint>() {
2521

2622
@Override
2723
public InjectionPoint get() {
28-
return CurrentInjectionPointProvider.EMPTY;
24+
return InjectionPointImpl.EMPTY;
2925
}
3026
};
3127

independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/EventBean.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public Set<Type> getTypes() {
2020
public Event<?> get(CreationalContext<Event<?>> creationalContext) {
2121
// Obtain current IP to get the required type and qualifiers
2222
InjectionPoint ip = InjectionPointProvider.getCurrent(creationalContext);
23-
return new EventImpl<>(ip.getType(), ip.getQualifiers(), ip);
23+
return ArcContainerImpl.instance().getEvent(ip.getType(), ip.getQualifiers(), ip);
2424
}
2525

2626
@Override

independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/EventProvider.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public EventProvider(Type eventType, Set<Annotation> eventQualifiers, InjectionP
2828

2929
@Override
3030
public Event<T> get(CreationalContext<Event<T>> creationalContext) {
31-
return new EventImpl<>(eventType, eventQualifiers, injectionPoint);
31+
return ArcContainerImpl.instance().getEvent(eventType, eventQualifiers, injectionPoint);
3232
}
3333

3434
}

0 commit comments

Comments
 (0)