Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

4.x: Ability to use HelidonTest in a meta-annotation #4918 #8759

Merged
merged 2 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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