Skip to content

Commit

Permalink
Make per-test test resources work on super-tests
Browse files Browse the repository at this point in the history
  • Loading branch information
FroMage committed Feb 17, 2021
1 parent 5df2647 commit ced0519
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 56 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -263,35 +263,7 @@ 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
for (Annotation reflAnnotation : testClassFromTCCL.getAnnotations()) {
for (Annotation annotationAnnotation : reflAnnotation.annotationType().getAnnotations()) {
if (annotationAnnotation.annotationType() == QuarkusTestResource.class) {
QuarkusTestResource testResource = (QuarkusTestResource) annotationAnnotation;

// NOTE: we don't need to check restrictToAnnotatedTest because by design config-based annotations
// are not discovered outside the test class, so they're restricted
Class<? extends QuarkusTestResourceLifecycleManager> testResourceClass = testResource.value();

ResourceArg[] argsAnnotationValue = testResource.initArgs();
Map<String, String> args;
if (argsAnnotationValue.length == 0) {
args = Collections.emptyMap();
} else {
args = new HashMap<>();
for (ResourceArg arg : argsAnnotationValue) {
args.put(arg.name(), arg.value());
}
}

boolean isParallel = testResource.parallel();

hasPerTestResources = true;
uniqueEntries.add(new TestResourceClassEntry(testResourceClass, args, reflAnnotation, isParallel));

break;
}
}
}
collectMetaAnnotations(testClassFromTCCL, uniqueEntries);
for (AnnotationInstance annotation : findQuarkusTestResourceInstances(testClass, index)) {
try {
Class<? extends QuarkusTestResourceLifecycleManager> testResourceClass = loadTestResourceClassFromTCCL(
Expand Down Expand Up @@ -330,6 +302,41 @@ private Set<TestResourceClassEntry> getUniqueTestResourceClassEntries(Class<?> t
return uniqueEntries;
}

private void collectMetaAnnotations(Class<?> testClassFromTCCL, Set<TestResourceClassEntry> uniqueEntries) {
while (!testClassFromTCCL.getName().equals("java.lang.Object")) {
for (Annotation reflAnnotation : testClassFromTCCL.getAnnotations()) {
for (Annotation annotationAnnotation : reflAnnotation.annotationType().getAnnotations()) {
if (annotationAnnotation.annotationType() == QuarkusTestResource.class) {
QuarkusTestResource testResource = (QuarkusTestResource) annotationAnnotation;

// NOTE: we don't need to check restrictToAnnotatedTest because by design config-based annotations
// are not discovered outside the test class, so they're restricted
Class<? extends QuarkusTestResourceLifecycleManager> testResourceClass = testResource.value();

ResourceArg[] argsAnnotationValue = testResource.initArgs();
Map<String, String> args;
if (argsAnnotationValue.length == 0) {
args = Collections.emptyMap();
} else {
args = new HashMap<>();
for (ResourceArg arg : argsAnnotationValue) {
args.put(arg.name(), arg.value());
}
}

boolean isParallel = testResource.parallel();

hasPerTestResources = true;
uniqueEntries.add(new TestResourceClassEntry(testResourceClass, args, reflAnnotation, isParallel));

break;
}
}
}
testClassFromTCCL = testClassFromTCCL.getSuperclass();
}
}

@SuppressWarnings("unchecked")
private Class<? extends QuarkusTestResourceLifecycleManager> loadTestResourceClassFromTCCL(String className) {
try {
Expand All @@ -341,9 +348,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();
}
Set<AnnotationInstance> testResourceAnnotations = new HashSet<>();
for (AnnotationInstance annotation : index.getAnnotations(DotName.createSimple(QuarkusTestResource.class.getName()))) {
if (keepTestResourceAnnotation(annotation, annotation.target().asClass(), testClass)) {
if (keepTestResourceAnnotation(annotation, annotation.target().asClass(), testClasses)) {
testResourceAnnotations.add(annotation);
}
}
Expand All @@ -352,7 +365,7 @@ private Collection<AnnotationInstance> findQuarkusTestResourceInstances(Class<?>
.getAnnotations(DotName.createSimple(QuarkusTestResource.List.class.getName()))) {
for (AnnotationInstance nestedAnnotation : annotation.value().asNestedArray()) {
// keep the list target
if (keepTestResourceAnnotation(nestedAnnotation, annotation.target().asClass(), testClass)) {
if (keepTestResourceAnnotation(nestedAnnotation, annotation.target().asClass(), testClasses)) {
testResourceAnnotations.add(nestedAnnotation);
}
}
Expand All @@ -365,10 +378,10 @@ public boolean hasPerTestResources() {
return hasPerTestResources;
}

private boolean keepTestResourceAnnotation(AnnotationInstance annotation, ClassInfo targetClass, Class<?> testClass) {
private boolean keepTestResourceAnnotation(AnnotationInstance annotation, ClassInfo targetClass, Set<String> testClasses) {
AnnotationValue restrict = annotation.value("restrictToAnnotatedTest");
if (restrict != null && restrict.asBoolean()) {
return testClass.getName().equals(targetClass.name().toString('.'));
return testClasses.contains(targetClass.name().toString('.'));
}
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ public class NativeTestExtension
private static Class<? extends QuarkusTestProfile> quarkusTestProfile;
private static Throwable firstException; //if this is set then it will be thrown from the very first test that is run, the rest are aborted

private static Class<?> currentJUnitTestClass;

private static boolean hasPerTestResources;

@Override
public void afterEach(ExtensionContext context) throws Exception {
if (!failedBoot) {
Expand Down Expand Up @@ -95,8 +99,11 @@ private ExtensionState ensureStarted(ExtensionContext extensionContext) {
selectedProfile = annotation.value();
}
boolean wrongProfile = !Objects.equals(selectedProfile, quarkusTestProfile);
if ((state == null && !failedBoot) || wrongProfile) {
if (wrongProfile) {
// we reload the test resources if we changed test class and if we had or will have per-test test resources
boolean reloadTestResources = !Objects.equals(extensionContext.getRequiredTestClass(), currentJUnitTestClass)
&& (hasPerTestResources || QuarkusTestExtension.hasPerTestResources(extensionContext));
if ((state == null && !failedBoot) || wrongProfile || reloadTestResources) {
if (wrongProfile || reloadTestResources) {
if (state != null) {
try {
state.close();
Expand All @@ -121,6 +128,7 @@ private ExtensionState ensureStarted(ExtensionContext extensionContext) {
private ExtensionState doNativeStart(ExtensionContext context, Class<? extends QuarkusTestProfile> profile)
throws Throwable {
quarkusTestProfile = profile;
currentJUnitTestClass = context.getRequiredTestClass();
TestResourceManager testResourceManager = null;
try {
Class<?> requiredTestClass = context.getRequiredTestClass();
Expand Down Expand Up @@ -160,6 +168,8 @@ private ExtensionState doNativeStart(ExtensionContext context, Class<? extends Q

testResourceManager = new TestResourceManager(requiredTestClass);
testResourceManager.init();
hasPerTestResources = testResourceManager.hasPerTestResources();

additional.putAll(testResourceManager.start());

NativeImageLauncher launcher = new NativeImageLauncher(requiredTestClass);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -651,27 +651,6 @@ private Class<? extends QuarkusTestProfile> getQuarkusTestProfile(ExtensionConte
return selectedProfile;
}

private boolean hasPerTestResources(ExtensionContext extensionContext) {
for (QuarkusTestResource testResource : extensionContext.getRequiredTestClass()
.getAnnotationsByType(QuarkusTestResource.class)) {
if (testResource.restrictToAnnotatedTest()) {
return true;
}
}
// scan for meta-annotations
for (Annotation annotation : extensionContext.getRequiredTestClass().getAnnotations()) {
// skip TestResource annotations
if (annotation.annotationType() != QuarkusTestResource.class) {
// look for a TestResource on the annotation itself
if (annotation.annotationType().getAnnotationsByType(QuarkusTestResource.class).length > 0) {
// meta-annotations are per-test scoped for now
return true;
}
}
}
return false;
}

private static ClassLoader setCCL(ClassLoader cl) {
final Thread thread = Thread.currentThread();
final ClassLoader original = thread.getContextClassLoader();
Expand Down Expand Up @@ -1205,4 +1184,32 @@ private static void resetHangTimeout() {
hangTaskKey = hangDetectionExecutor.schedule(hangDetectionTask, hangTimeout.toMillis(), TimeUnit.MILLISECONDS);
}
}

static boolean hasPerTestResources(ExtensionContext extensionContext) {
return hasPerTestResources(extensionContext.getRequiredTestClass());
}

public static boolean hasPerTestResources(Class<?> requiredTestClass) {
while (requiredTestClass != Object.class) {
for (QuarkusTestResource testResource : requiredTestClass.getAnnotationsByType(QuarkusTestResource.class)) {
if (testResource.restrictToAnnotatedTest()) {
return true;
}
}
// scan for meta-annotations
for (Annotation annotation : requiredTestClass.getAnnotations()) {
// skip TestResource annotations
if (annotation.annotationType() != QuarkusTestResource.class) {
// look for a TestResource on the annotation itself
if (annotation.annotationType().getAnnotationsByType(QuarkusTestResource.class).length > 0) {
// meta-annotations are per-test scoped for now
return true;
}
}
}
// look up
requiredTestClass = requiredTestClass.getSuperclass();
}
return false;
}
}

0 comments on commit ced0519

Please sign in to comment.