Skip to content

Commit

Permalink
Support test resources with restrict to annotatedClass in @nested class
Browse files Browse the repository at this point in the history
Fix #26639

(cherry picked from commit ea838c6)
  • Loading branch information
Sgitario authored and gsmet committed Jul 19, 2022
1 parent 84ba96f commit 8e75990
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 15 deletions.
@@ -0,0 +1,70 @@
package io.quarkus.it.main;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Map;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;

import io.quarkus.test.common.QuarkusTestResource;
import io.quarkus.test.common.QuarkusTestResourceLifecycleManager;
import io.quarkus.test.junit.QuarkusTest;

/**
* Tests {@link Nested} support of {@link QuarkusTest}. Notes:
* <ul>
* <li>to avoid unexpected execution order, don't use surefire's {@code -Dtest=...}, use {@code -Dgroups=nested} instead</li>
* <li>order of nested test classes is reversed by JUnit (and there's no way to enforce a specific order)</li>
* </ul>
*/
@QuarkusTest
@Tag("nested")
@QuarkusTestResource(value = QuarkusTestNestedWithResourcesTestCase.DummyTestResource.class, restrictToAnnotatedClass = true)
public class QuarkusTestNestedWithResourcesTestCase {

@InjectDummyString
String bar;

@Test
public void testBarFromOuter() {
Assertions.assertEquals("bar", bar);
}

@Nested
class NestedTestClass {

@Test
public void testBarFromNested() {
Assertions.assertEquals("bar", bar);
}
}

@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD })
public @interface InjectDummyString {
}

public static class DummyTestResource implements QuarkusTestResourceLifecycleManager {

@Override
public Map<String, String> start() {
return null;
}

@Override
public void stop() {

}

@Override
public void inject(TestInjector testInjector) {
testInjector.injectIntoFields("bar",
new TestInjector.AnnotatedAndMatchesType(InjectDummyString.class, String.class));
}
}
}
Expand Up @@ -21,6 +21,7 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Function;
import java.util.function.Predicate;

import org.eclipse.microprofile.config.spi.ConfigProviderResolver;
Expand Down Expand Up @@ -256,9 +257,10 @@ private Set<TestResourceClassEntry> getUniqueTestResourceClassEntries(Class<?> t
}
// handle meta-annotations: in this case we must rely on reflection because meta-annotations are not indexed
// because they are not in the user's test folder but come from test extensions
collectMetaAnnotations(testClassFromTCCL, uniqueEntries);
collectMetaAnnotations(testClassFromTCCL, Class::getSuperclass, uniqueEntries);
collectMetaAnnotations(testClassFromTCCL, Class::getEnclosingClass, uniqueEntries);
if (profileClassFromTCCL != null) {
collectMetaAnnotations(profileClassFromTCCL, uniqueEntries);
collectMetaAnnotations(profileClassFromTCCL, Class::getSuperclass, uniqueEntries);
}
for (AnnotationInstance annotation : findQuarkusTestResourceInstances(testClass, index)) {
try {
Expand Down Expand Up @@ -298,8 +300,9 @@ private Set<TestResourceClassEntry> getUniqueTestResourceClassEntries(Class<?> t
return uniqueEntries;
}

private void collectMetaAnnotations(Class<?> testClassFromTCCL, Set<TestResourceClassEntry> uniqueEntries) {
while (!testClassFromTCCL.getName().equals("java.lang.Object")) {
private void collectMetaAnnotations(Class<?> testClassFromTCCL, Function<Class<?>, Class<?>> next,
Set<TestResourceClassEntry> uniqueEntries) {
while (testClassFromTCCL != null && !testClassFromTCCL.getName().equals("java.lang.Object")) {
for (Annotation metaAnnotation : testClassFromTCCL.getAnnotations()) {
for (Annotation ann : metaAnnotation.annotationType().getAnnotations()) {
if (ann.annotationType() == QuarkusTestResource.class) {
Expand All @@ -315,7 +318,7 @@ private void collectMetaAnnotations(Class<?> testClassFromTCCL, Set<TestResource
}
}
}
testClassFromTCCL = testClassFromTCCL.getSuperclass();
testClassFromTCCL = next.apply(testClassFromTCCL);
}
}

Expand Down Expand Up @@ -353,9 +356,15 @@ private Class<? extends QuarkusTestResourceLifecycleManager> loadTestResourceCla
private Collection<AnnotationInstance> findQuarkusTestResourceInstances(Class<?> testClass, IndexView index) {
// collect all test supertypes for matching per-test targets
Set<String> testClasses = new HashSet<>();
while (testClass != Object.class) {
testClasses.add(testClass.getName());
testClass = testClass.getSuperclass();
Class<?> current = testClass;
while (current != Object.class) {
testClasses.add(current.getName());
current = current.getSuperclass();
}
current = testClass.getEnclosingClass();
while (current != null) {
testClasses.add(current.getName());
current = current.getEnclosingClass();
}
Set<AnnotationInstance> testResourceAnnotations = new LinkedHashSet<>();
for (AnnotationInstance annotation : index.getAnnotations(DotName.createSimple(QuarkusTestResource.class.getName()))) {
Expand Down
Expand Up @@ -771,20 +771,15 @@ private void initTestState(ExtensionContext extensionContext, ExtensionState sta
outerInstances.add(actualTestInstance);
actualTestInstance = declaredConstructor.newInstance(actualTestInstance);
} else {
Object outerInstance = runningQuarkusApplication.instance(outerClass);
Object outerInstance = createActualTestInstance(outerClass, state);
actualTestInstance = declaredConstructor.newInstance(outerInstance);
outerInstances.add(outerInstance);
}
} else {
outerInstances.clear();
actualTestInstance = runningQuarkusApplication.instance(actualTestClass);
actualTestInstance = createActualTestInstance(actualTestClass, state);
}

Class<?> resM = Thread.currentThread().getContextClassLoader().loadClass(TestHTTPResourceManager.class.getName());
resM.getDeclaredMethod("inject", Object.class, List.class).invoke(null, actualTestInstance,
testHttpEndpointProviders);
state.testResourceManager.getClass().getMethod("inject", Object.class).invoke(state.testResourceManager,
actualTestInstance);
for (Object afterConstructCallback : afterConstructCallbacks) {
afterConstructCallback.getClass().getMethod("afterConstruct", Object.class).invoke(afterConstructCallback,
actualTestInstance);
Expand All @@ -801,6 +796,19 @@ private void initTestState(ExtensionContext extensionContext, ExtensionState sta
}
}

private Object createActualTestInstance(Class<?> testClass, ExtensionState state)
throws ClassNotFoundException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
Object testInstance = runningQuarkusApplication.instance(testClass);

Class<?> resM = Thread.currentThread().getContextClassLoader().loadClass(TestHTTPResourceManager.class.getName());
resM.getDeclaredMethod("inject", Object.class, List.class).invoke(null, testInstance,
testHttpEndpointProviders);
state.testResourceManager.getClass().getMethod("inject", Object.class).invoke(state.testResourceManager,
testInstance);

return testInstance;
}

@Override
public void interceptBeforeEachMethod(Invocation<Void> invocation, ReflectiveInvocationContext<Method> invocationContext,
ExtensionContext extensionContext) throws Throwable {
Expand Down

0 comments on commit 8e75990

Please sign in to comment.