Skip to content

Commit

Permalink
4.x: Ability to use HelidonTest in a meta-annotation #4918 (#8759)
Browse files Browse the repository at this point in the history
Signed-off-by: Jorge Bescos Gascon <[email protected]>
(cherry picked from commit bd5bab5)
  • Loading branch information
jbescos authored and danielkec committed Dec 20, 2024
1 parent 4fe8cab commit 22554e8
Show file tree
Hide file tree
Showing 4 changed files with 299 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,13 @@
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import io.helidon.config.mp.MpConfigSources;
import io.helidon.microprofile.server.JaxRsCdiExtension;
Expand Down Expand Up @@ -88,7 +90,8 @@ class HelidonJunitExtension implements BeforeAllCallback,
InvocationInterceptor,
ParameterResolver {
private static final Set<Class<? extends Annotation>> HELIDON_TEST_ANNOTATIONS =
Set.of(AddBean.class, AddConfig.class, AddExtension.class, Configuration.class, AddJaxRs.class);
Set.of(AddBean.class, AddConfig.class, AddExtension.class, Configuration.class,
AddJaxRs.class, AddConfigBlock.class);
private static final Map<Class<? extends Annotation>, Annotation> BEAN_DEFINING = new HashMap<>();

static {
Expand All @@ -115,24 +118,26 @@ class HelidonJunitExtension implements BeforeAllCallback,
public void beforeAll(ExtensionContext context) {
testClass = context.getRequiredTestClass();

AddConfig[] configs = getAnnotations(testClass, AddConfig.class);
List<Annotation> metaAnnotations = extractMetaAnnotations(testClass);

AddConfig[] configs = getAnnotations(testClass, AddConfig.class, metaAnnotations);
classLevelConfigMeta.addConfig(configs);
classLevelConfigMeta.configuration(testClass.getAnnotation(Configuration.class));
classLevelConfigMeta.addConfigBlock(testClass.getAnnotation(AddConfigBlock.class));
classLevelConfigMeta.configuration(getAnnotation(testClass, Configuration.class, metaAnnotations));
classLevelConfigMeta.addConfigBlock(getAnnotation(testClass, AddConfigBlock.class, metaAnnotations));
configProviderResolver = ConfigProviderResolver.instance();

AddExtension[] extensions = getAnnotations(testClass, AddExtension.class);
AddExtension[] extensions = getAnnotations(testClass, AddExtension.class, metaAnnotations);
classLevelExtensions.addAll(Arrays.asList(extensions));

AddBean[] beans = getAnnotations(testClass, AddBean.class);
AddBean[] beans = getAnnotations(testClass, AddBean.class, metaAnnotations);
classLevelBeans.addAll(Arrays.asList(beans));

HelidonTest testAnnot = testClass.getAnnotation(HelidonTest.class);
if (testAnnot != null) {
resetPerTest = testAnnot.resetPerTest();
}

DisableDiscovery discovery = testClass.getAnnotation(DisableDiscovery.class);
DisableDiscovery discovery = getAnnotation(testClass, DisableDiscovery.class, metaAnnotations);
if (discovery != null) {
classLevelDisableDiscovery = discovery.value();
}
Expand All @@ -145,7 +150,7 @@ public void beforeAll(ExtensionContext context) {
validatePerClass();

// add beans when using JaxRS
AddJaxRs addJaxRsAnnotation = testClass.getAnnotation(AddJaxRs.class);
AddJaxRs addJaxRsAnnotation = getAnnotation(testClass, AddJaxRs.class, metaAnnotations);
if (addJaxRsAnnotation != null){
classLevelExtensions.add(ProcessAllAnnotatedTypesLiteral.INSTANCE);
classLevelExtensions.add(ServerCdiExtensionLiteral.INSTANCE);
Expand All @@ -164,13 +169,42 @@ public void beforeAll(ExtensionContext context) {
}
}

private List<Annotation> extractMetaAnnotations(Class<?> testClass) {
Annotation[] testAnnotations = testClass.getAnnotations();
for (Annotation testAnnotation : testAnnotations) {
List<Annotation> annotations = List.of(testAnnotation.annotationType().getAnnotations());
List<Class<?>> annotationsClass = annotations.stream()
.map(a -> a.annotationType()).collect(Collectors.toList());
if (!Collections.disjoint(HELIDON_TEST_ANNOTATIONS, annotationsClass)) {
// Contains at least one of HELIDON_TEST_ANNOTATIONS
return annotations;
}
}
return List.of();
}

private <T extends Annotation> T getAnnotation(Class<?> testClass, Class<T> annotClass,
List<Annotation> metaAnnotations) {
T annotation = testClass.getAnnotation(annotClass);
if (annotation == null) {
List<T> byType = annotationsByType(annotClass, metaAnnotations);
if (!byType.isEmpty()) {
annotation = byType.get(0);
}
}
return annotation;
}

@SuppressWarnings("unchecked")
private <T extends Annotation> T[] getAnnotations(Class<?> testClass, Class<T> annotClass) {
private <T extends Annotation> T[] getAnnotations(Class<?> testClass, Class<T> annotClass,
List<Annotation> metaAnnotations) {
// inherited does not help, as it only returns annot from superclass if
// child has none
T[] directAnnotations = testClass.getAnnotationsByType(annotClass);

List<T> allAnnotations = new ArrayList<>(List.of(directAnnotations));
// Include meta annotations
allAnnotations.addAll(annotationsByType(annotClass, metaAnnotations));

Class<?> superClass = testClass.getSuperclass();
while (superClass != null) {
Expand All @@ -187,6 +221,16 @@ private <T extends Annotation> T[] getAnnotations(Class<?> testClass, Class<T> a
return (T[]) result;
}

private <T extends Annotation> List<T> annotationsByType(Class<T> annotClass, List<Annotation> metaAnnotations) {
List<T> byType = new ArrayList<>();
for (Annotation annotation : metaAnnotations) {
if (annotation.annotationType() == annotClass) {
byType.add((T) annotation);
}
}
return byType;
}

@Override
public void beforeEach(ExtensionContext context) throws Exception {
if (resetPerTest) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,13 @@
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import io.helidon.config.mp.MpConfigSources;
import io.helidon.microprofile.server.JaxRsCdiExtension;
Expand Down Expand Up @@ -73,8 +75,9 @@
*/
public class HelidonTestNgListener implements IClassListener, ITestListener {

private static final Set<Class<? extends Annotation>> TEST_ANNOTATIONS = Set.of(
AddBean.class, AddConfig.class, AddExtension.class, Configuration.class, AddJaxRs.class);
private static final Set<Class<? extends Annotation>> TEST_ANNOTATIONS =
Set.of(AddBean.class, AddConfig.class, AddExtension.class,
Configuration.class, AddJaxRs.class, AddConfigBlock.class);

private static final Map<Class<? extends Annotation>, AnnotationLiteral<?>> BEAN_DEFINING = Map.of(
ApplicationScoped.class, ApplicationScoped.Literal.INSTANCE,
Expand All @@ -87,6 +90,7 @@ public class HelidonTestNgListener implements IClassListener, ITestListener {
private ConfigMeta classLevelConfigMeta = new ConfigMeta();
private boolean classLevelDisableDiscovery = false;
private boolean resetPerTest;

private Class<?> testClass;
private Object testInstance;
private ConfigProviderResolver configProviderResolver;
Expand All @@ -95,26 +99,29 @@ public class HelidonTestNgListener implements IClassListener, ITestListener {

@Override
public void onBeforeClass(ITestClass iTestClass) {

testClass = iTestClass.getRealClass();

AddConfig[] configs = getAnnotations(testClass, AddConfig.class);
List<Annotation> metaAnnotations = extractMetaAnnotations(testClass);

AddConfig[] configs = getAnnotations(testClass, AddConfig.class, metaAnnotations);
classLevelConfigMeta.addConfig(configs);
classLevelConfigMeta.configuration(testClass.getAnnotation(Configuration.class));
classLevelConfigMeta.addConfigBlock(testClass.getAnnotation(AddConfigBlock.class));
classLevelConfigMeta.configuration(getAnnotation(testClass, Configuration.class, metaAnnotations));
classLevelConfigMeta.addConfigBlock(getAnnotation(testClass, AddConfigBlock.class, metaAnnotations));
configProviderResolver = ConfigProviderResolver.instance();

AddExtension[] extensions = getAnnotations(testClass, AddExtension.class);
AddExtension[] extensions = getAnnotations(testClass, AddExtension.class, metaAnnotations);
classLevelExtensions.addAll(Arrays.asList(extensions));

AddBean[] beans = getAnnotations(testClass, AddBean.class);
AddBean[] beans = getAnnotations(testClass, AddBean.class, metaAnnotations);
classLevelBeans.addAll(Arrays.asList(beans));

HelidonTest testAnnot = testClass.getAnnotation(HelidonTest.class);
if (testAnnot != null) {
resetPerTest = testAnnot.resetPerTest();
}

DisableDiscovery discovery = testClass.getAnnotation(DisableDiscovery.class);
DisableDiscovery discovery = getAnnotation(testClass, DisableDiscovery.class, metaAnnotations);
if (discovery != null) {
classLevelDisableDiscovery = discovery.value();
}
Expand All @@ -126,7 +133,7 @@ public void onBeforeClass(ITestClass iTestClass) {
validatePerClass();

// add beans when using JaxRS
AddJaxRs addJaxRsAnnotation = testClass.getAnnotation(AddJaxRs.class);
AddJaxRs addJaxRsAnnotation = getAnnotation(testClass, AddJaxRs.class, metaAnnotations);
if (addJaxRsAnnotation != null){
classLevelExtensions.add(ProcessAllAnnotatedTypesLiteral.INSTANCE);
classLevelExtensions.add(ServerCdiExtensionLiteral.INSTANCE);
Expand All @@ -147,6 +154,7 @@ public void onBeforeClass(ITestClass iTestClass) {
}
}


@Override
public void onAfterClass(ITestClass testClass) {
if (!resetPerTest) {
Expand All @@ -157,6 +165,7 @@ public void onAfterClass(ITestClass testClass) {

@Override
public void onTestStart(ITestResult result) {

if (resetPerTest) {
Method method = result.getMethod().getConstructorOrMethod().getMethod();
AddConfig[] configs = method.getAnnotationsByType(AddConfig.class);
Expand Down Expand Up @@ -268,6 +277,7 @@ private void releaseConfig() {
if (configProviderResolver != null && config != null) {
configProviderResolver.releaseConfig(config);
config = null;

classLevelExtensions = new ArrayList<>();
classLevelBeans = new ArrayList<>();
classLevelConfigMeta = new ConfigMeta();
Expand Down Expand Up @@ -312,13 +322,52 @@ private void stopContainer() {
}
}

private List<Annotation> extractMetaAnnotations(Class<?> testClass) {
Annotation[] testAnnotations = testClass.getAnnotations();
for (Annotation testAnnotation : testAnnotations) {
List<Annotation> annotations = List.of(testAnnotation.annotationType().getAnnotations());
List<Class<?>> annotationsClass = annotations.stream()
.map(a -> a.annotationType()).collect(Collectors.toList());
if (!Collections.disjoint(TEST_ANNOTATIONS, annotationsClass)) {
// Contains at least one of HELIDON_TEST_ANNOTATIONS
return annotations;
}
}
return List.of();
}

private <T extends Annotation> List<T> annotationsByType(Class<T> annotClass, List<Annotation> metaAnnotations) {
List<T> byType = new ArrayList<>();
for (Annotation annotation : metaAnnotations) {
if (annotation.annotationType() == annotClass) {
byType.add((T) annotation);
}
}
return byType;
}

private <T extends Annotation> T getAnnotation(Class<?> testClass, Class<T> annotClass,
List<Annotation> metaAnnotations) {
T annotation = testClass.getAnnotation(annotClass);
if (annotation == null) {
List<T> byType = annotationsByType(annotClass, metaAnnotations);
if (!byType.isEmpty()) {
annotation = byType.get(0);
}
}
return annotation;
}

@SuppressWarnings("unchecked")
private <T extends Annotation> T[] getAnnotations(Class<?> testClass, Class<T> annotClass) {
private <T extends Annotation> T[] getAnnotations(Class<?> testClass, Class<T> annotClass,
List<Annotation> metaAnnotations) {
// inherited does not help, as it only returns annot from superclass if
// child has none
T[] directAnnotations = testClass.getAnnotationsByType(annotClass);

List<T> allAnnotations = new ArrayList<>(List.of(directAnnotations));
// Include meta annotations
allAnnotations.addAll(annotationsByType(annotClass, metaAnnotations));

Class<?> superClass = testClass.getSuperclass();
while (superClass != null) {
Expand Down Expand Up @@ -406,7 +455,7 @@ void registerOtherBeans(@Observes AfterBeanDiscovery event) {
.addType(jakarta.ws.rs.client.WebTarget.class)
.scope(ApplicationScoped.class)
.produceWith(context -> ClientBuilder.newClient().target(clientUri()));
}
}

void registerAddedBeans(@Observes BeforeBeanDiscovery event) {
for (AddBean beanDef : addBeans) {
Expand Down Expand Up @@ -461,12 +510,12 @@ public void inject(T testInstance, CreationalContext<T> cc) {
@Override
public void postConstruct(T testInstance) {
delegate.postConstruct(testInstance);
}
}

@Override
public void preDestroy(T testInstance) {
delegate.preDestroy(testInstance);
}
}

@Override
public T produce(CreationalContext<T> cc) {
Expand Down Expand Up @@ -521,10 +570,12 @@ void addConfigBlock(AddConfigBlock config) {

ConfigMeta nextMethod() {
ConfigMeta methodMeta = new ConfigMeta();

methodMeta.additionalKeys.putAll(this.additionalKeys);
methodMeta.additionalSources.addAll(this.additionalSources);
methodMeta.useExisting = this.useExisting;
methodMeta.profile = this.profile;

return methodMeta;
}
}
Expand Down
Loading

0 comments on commit 22554e8

Please sign in to comment.