Skip to content

Commit

Permalink
Merge pull request #27179 from geoand/#26733
Browse files Browse the repository at this point in the history
Ensure that Quarkus can mock final methods of beans created by producers
  • Loading branch information
geoand authored Aug 8, 2022
2 parents 837a04b + 3390d03 commit 2493ed7
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 52 deletions.
Original file line number Diff line number Diff line change
@@ -1,15 +1,7 @@
package io.quarkus.arc.test.unproxyable;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

import javax.annotation.PostConstruct;
import javax.enterprise.context.Dependent;
import javax.enterprise.context.RequestScoped;
import javax.enterprise.event.Event;
import javax.enterprise.event.Observes;
import javax.inject.Inject;
import javax.inject.Singleton;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
Expand All @@ -19,14 +11,15 @@
import io.quarkus.arc.ArcContainer;
import io.quarkus.arc.InstanceHandle;
import io.quarkus.arc.ManagedContext;
import io.quarkus.arc.Unremovable;
import io.quarkus.test.QuarkusUnitTest;

public class RequestScopedFinalMethodsTest {

@RegisterExtension
public static QuarkusUnitTest container = new QuarkusUnitTest()
.withApplicationRoot((jar) -> jar
.addClasses(RequestScopedBean.class));
.addClasses(RequestScopedBean.class, OtherRequestScopedBean.class, OtherRequestScopeBeanProducer.class));

@Test
public void testRequestScopedBeanWorksProperly() {
Expand All @@ -36,22 +29,35 @@ public void testRequestScopedBeanWorksProperly() {

InstanceHandle<RequestScopedBean> handle = container.instance(RequestScopedBean.class);
Assertions.assertTrue(handle.isAvailable());
InstanceHandle<OtherRequestScopedBean> otherHandle = container.instance(OtherRequestScopedBean.class);
Assertions.assertTrue(otherHandle.isAvailable());

RequestScopedBean bean = handle.get();
Assertions.assertNull(bean.getProp());
bean.setProp(100);
Assertions.assertEquals(100, bean.getProp());

OtherRequestScopedBean otherBean = otherHandle.get();
Assertions.assertNull(otherBean.getProp());
otherBean.setProp(100);
Assertions.assertEquals(100, otherBean.getProp());

requestContext.terminate();
requestContext.activate();

handle = container.instance(RequestScopedBean.class);
bean = handle.get();
Assertions.assertTrue(handle.isAvailable());
bean = handle.get();
Assertions.assertNull(bean.getProp());

otherHandle = container.instance(OtherRequestScopedBean.class);
Assertions.assertTrue(otherHandle.isAvailable());
otherBean = otherHandle.get();
Assertions.assertNull(otherBean.getProp());
}

@RequestScoped
@Unremovable
static class RequestScopedBean {
private Integer prop = null;

Expand All @@ -64,41 +70,26 @@ public final void setProp(Integer prop) {
}
}

@Dependent
static class StringProducer {

@Inject
Event<String> event;
static class OtherRequestScopedBean {
private Integer prop = null;

void fire(String value) {
event.fire(value);
public final Integer getProp() {
return prop;
}

}

@Singleton
static class StringObserver {

private List<Integer> events;

@Inject
RequestScopedBean requestScopedBean;

@PostConstruct
void init() {
events = new CopyOnWriteArrayList<>();
public final void setProp(Integer prop) {
this.prop = prop;
}
}

void observeSync(@Observes Integer value) {
Integer oldValue = requestScopedBean.getProp();
Integer newValue = oldValue == null ? value : value + oldValue;
requestScopedBean.setProp(newValue);
events.add(newValue);
}
@Dependent
static class OtherRequestScopeBeanProducer {

List<Integer> getEvents() {
return events;
@RequestScoped
@Unremovable
public OtherRequestScopedBean produce() {
return new OtherRequestScopedBean();
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -325,25 +325,20 @@ Collection<MethodInfo> getDelegatingMethods(BeanInfo bean, Consumer<BytecodeTran
if (bean.isClassBean()) {
Map<String, Set<Methods.NameAndDescriptor>> methodsFromWhichToRemoveFinal = new HashMap<>();
ClassInfo classInfo = bean.getTarget().get().asClass();
Methods.addDelegatingMethods(index, classInfo,
methods, methodsFromWhichToRemoveFinal, transformUnproxyableClasses);
if (!methodsFromWhichToRemoveFinal.isEmpty()) {
for (Map.Entry<String, Set<Methods.NameAndDescriptor>> entry : methodsFromWhichToRemoveFinal.entrySet()) {
String className = entry.getKey();
bytecodeTransformerConsumer.accept(new BytecodeTransformer(className,
new Methods.RemoveFinalFromMethod(className, entry.getValue())));
}
}
addDelegatesAndTrasformIfNecessary(bytecodeTransformerConsumer, transformUnproxyableClasses, methods, index,
methodsFromWhichToRemoveFinal, classInfo);
} else if (bean.isProducerMethod()) {
Map<String, Set<Methods.NameAndDescriptor>> methodsFromWhichToRemoveFinal = new HashMap<>();
MethodInfo producerMethod = bean.getTarget().get().asMethod();
ClassInfo returnTypeClass = getClassByName(index, producerMethod.returnType());
Methods.addDelegatingMethods(index, returnTypeClass, methods, null,
transformUnproxyableClasses);
addDelegatesAndTrasformIfNecessary(bytecodeTransformerConsumer, transformUnproxyableClasses, methods, index,
methodsFromWhichToRemoveFinal, returnTypeClass);
} else if (bean.isProducerField()) {
Map<String, Set<Methods.NameAndDescriptor>> methodsFromWhichToRemoveFinal = new HashMap<>();
FieldInfo producerField = bean.getTarget().get().asField();
ClassInfo fieldClass = getClassByName(index, producerField.type());
Methods.addDelegatingMethods(index, fieldClass, methods, null,
transformUnproxyableClasses);
addDelegatesAndTrasformIfNecessary(bytecodeTransformerConsumer, transformUnproxyableClasses, methods, index,
methodsFromWhichToRemoveFinal, fieldClass);
} else if (bean.isSynthetic()) {
Methods.addDelegatingMethods(index, bean.getImplClazz(), methods, null,
transformUnproxyableClasses);
Expand All @@ -352,6 +347,22 @@ Collection<MethodInfo> getDelegatingMethods(BeanInfo bean, Consumer<BytecodeTran
return methods.values();
}

private void addDelegatesAndTrasformIfNecessary(Consumer<BytecodeTransformer> bytecodeTransformerConsumer,
boolean transformUnproxyableClasses,
Map<Methods.MethodKey, MethodInfo> methods, IndexView index,
Map<String, Set<Methods.NameAndDescriptor>> methodsFromWhichToRemoveFinal,
ClassInfo fieldClass) {
Methods.addDelegatingMethods(index, fieldClass, methods, methodsFromWhichToRemoveFinal,
transformUnproxyableClasses);
if (!methodsFromWhichToRemoveFinal.isEmpty()) {
for (Map.Entry<String, Set<Methods.NameAndDescriptor>> entry : methodsFromWhichToRemoveFinal.entrySet()) {
String className = entry.getKey();
bytecodeTransformerConsumer.accept(new BytecodeTransformer(className,
new Methods.RemoveFinalFromMethod(className, entry.getValue())));
}
}
}

private DotName getApplicationClassTestName(BeanInfo bean) {
DotName testedName;
// For producers we need to test the produced type
Expand Down

0 comments on commit 2493ed7

Please sign in to comment.