diff --git a/test-framework/junit5/src/main/java/io/quarkus/test/junit/util/QuarkusTestProfileAwareClassOrderer.java b/test-framework/junit5/src/main/java/io/quarkus/test/junit/util/QuarkusTestProfileAwareClassOrderer.java
index 0b98384513a01..9ec802da34003 100644
--- a/test-framework/junit5/src/main/java/io/quarkus/test/junit/util/QuarkusTestProfileAwareClassOrderer.java
+++ b/test-framework/junit5/src/main/java/io/quarkus/test/junit/util/QuarkusTestProfileAwareClassOrderer.java
@@ -1,22 +1,30 @@
package io.quarkus.test.junit.util;
+import java.util.Arrays;
import java.util.Comparator;
import java.util.Optional;
import org.junit.jupiter.api.ClassDescriptor;
import org.junit.jupiter.api.ClassOrderer;
import org.junit.jupiter.api.ClassOrdererContext;
+import org.junit.jupiter.api.Nested;
+import io.quarkus.test.common.QuarkusTestResource;
import io.quarkus.test.junit.QuarkusIntegrationTest;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.junit.TestProfile;
+import io.quarkus.test.junit.main.QuarkusMainTest;
/**
- * A {@link ClassOrderer} that orders {@link QuarkusTest} and {@link QuarkusIntegrationTest} classes for minimum Quarkus
- * restarts by grouping them by their {@link TestProfile}.
+ * A {@link ClassOrderer} that orders {@link QuarkusTest}, {@link QuarkusIntegrationTest} and {@link QuarkusMainTest} classes
+ * for minimum Quarkus
+ * restarts by grouping them by their {@link TestProfile} and {@link QuarkusTestResource} annotation(s).
*
* By default, Quarkus*Tests not using any profile come first, then classes using a profile (in groups) and then all other
- * non-Quarkus tests (e.g. plain unit tests).
+ * non-Quarkus tests (e.g. plain unit tests).
+ * Quarkus*Tests with {@linkplain QuarkusTestResource#restrictToAnnotatedClass() restricted} {@code QuarkusTestResource} come
+ * after tests with profiles and Quarkus*Tests with only unrestricted resources are handled like tests without a profile (come
+ * first).
*
* Internally, ordering is based on three prefixes that are prepended to the fully qualified name of the respective class, with
* the fully qualified class name of the {@link io.quarkus.test.junit.QuarkusTestProfile QuarkusTestProfile} as an infix (if
@@ -30,31 +38,39 @@
*
* Limitations:
*
- * - This orderer does not (yet) consider {@linkplain io.quarkus.test.common.QuarkusTestResource#restrictToAnnotatedClass()
- * test resources that are restricted to the annotated class}.
* - Only JUnit5 test classes are subject to ordering, e.g. ArchUnit test classes are not passed to this orderer.
+ * - This orderer does not handle {@link Nested} test classes.
*
*/
public class QuarkusTestProfileAwareClassOrderer implements ClassOrderer {
protected static final String DEFAULT_ORDER_PREFIX_QUARKUS_TEST = "20_";
protected static final String DEFAULT_ORDER_PREFIX_QUARKUS_TEST_WITH_PROFILE = "40_";
+ protected static final String DEFAULT_ORDER_PREFIX_QUARKUS_TEST_WITH_RESTRICTED_RES = "45_";
protected static final String DEFAULT_ORDER_PREFIX_NON_QUARKUS_TEST = "60_";
static final String CFGKEY_ORDER_PREFIX_QUARKUS_TEST = "quarkus.test.orderer.prefix.quarkus-test";
static final String CFGKEY_ORDER_PREFIX_QUARKUS_TEST_WITH_PROFILE = "quarkus.test.orderer.prefix.quarkus-test-with-profile";
+ static final String CFGKEY_ORDER_PREFIX_QUARKUS_TEST_WITH_RESTRICTED_RES = "quarkus.test.orderer.prefix.quarkus-test-with-restricted-resource";
static final String CFGKEY_ORDER_PREFIX_NON_QUARKUS_TEST = "quarkus.test.orderer.prefix.non-quarkus-test";
@Override
public void orderClasses(ClassOrdererContext context) {
- if (context.getClassDescriptors().size() <= 1) {
+ // don't do anything if there is just one test class or the current order request is for @Nested tests
+ if (context.getClassDescriptors().size() <= 1 || context.getClassDescriptors().get(0).isAnnotated(Nested.class)) {
return;
}
- var prefixQuarkusTest = context.getConfigurationParameter(CFGKEY_ORDER_PREFIX_QUARKUS_TEST)
+ var prefixQuarkusTest = context
+ .getConfigurationParameter(CFGKEY_ORDER_PREFIX_QUARKUS_TEST)
.orElse(DEFAULT_ORDER_PREFIX_QUARKUS_TEST);
- var prefixQuarkusTestWithProfile = context.getConfigurationParameter(CFGKEY_ORDER_PREFIX_QUARKUS_TEST_WITH_PROFILE)
+ var prefixQuarkusTestWithProfile = context
+ .getConfigurationParameter(CFGKEY_ORDER_PREFIX_QUARKUS_TEST_WITH_PROFILE)
.orElse(DEFAULT_ORDER_PREFIX_QUARKUS_TEST_WITH_PROFILE);
- var prefixNonQuarkusTest = context.getConfigurationParameter(CFGKEY_ORDER_PREFIX_NON_QUARKUS_TEST)
+ var prefixQuarkusTestWithRestrictedResource = context
+ .getConfigurationParameter(CFGKEY_ORDER_PREFIX_QUARKUS_TEST_WITH_RESTRICTED_RES)
+ .orElse(DEFAULT_ORDER_PREFIX_QUARKUS_TEST_WITH_RESTRICTED_RES);
+ var prefixNonQuarkusTest = context
+ .getConfigurationParameter(CFGKEY_ORDER_PREFIX_NON_QUARKUS_TEST)
.orElse(DEFAULT_ORDER_PREFIX_NON_QUARKUS_TEST);
context.getClassDescriptors().sort(Comparator.comparing(classDescriptor -> {
@@ -64,16 +80,33 @@ public void orderClasses(ClassOrdererContext context) {
}
var testClassName = classDescriptor.getTestClass().getName();
if (classDescriptor.isAnnotated(QuarkusTest.class)
- || classDescriptor.isAnnotated(QuarkusIntegrationTest.class)) {
+ || classDescriptor.isAnnotated(QuarkusIntegrationTest.class)
+ || classDescriptor.isAnnotated(QuarkusMainTest.class)) {
return classDescriptor.findAnnotation(TestProfile.class)
.map(TestProfile::value)
.map(profileClass -> prefixQuarkusTestWithProfile + profileClass.getName() + "@" + testClassName)
- .orElseGet(() -> prefixQuarkusTest + testClassName);
+ .orElseGet(() -> {
+ var prefix = hasRestrictedResource(classDescriptor)
+ ? prefixQuarkusTestWithRestrictedResource
+ : prefixQuarkusTest;
+ return prefix + testClassName;
+ });
}
return prefixNonQuarkusTest + testClassName;
}));
}
+ private boolean hasRestrictedResource(ClassDescriptor classDescriptor) {
+ return classDescriptor.findRepeatableAnnotations(QuarkusTestResource.class).stream()
+ .anyMatch(res -> res.restrictToAnnotatedClass() || isMetaTestResource(res, classDescriptor));
+ }
+
+ private boolean isMetaTestResource(QuarkusTestResource resource, ClassDescriptor classDescriptor) {
+ return Arrays.stream(classDescriptor.getTestClass().getAnnotationsByType(QuarkusTestResource.class))
+ .map(QuarkusTestResource::value)
+ .noneMatch(resource.value()::equals);
+ }
+
/**
* Template method that provides an optional custom order key for the given {@code classDescriptor}.
*
diff --git a/test-framework/junit5/src/main/resources/junit-platform.properties b/test-framework/junit5/src/main/resources/junit-platform.properties
new file mode 100644
index 0000000000000..0823c6b870646
--- /dev/null
+++ b/test-framework/junit5/src/main/resources/junit-platform.properties
@@ -0,0 +1 @@
+junit.jupiter.testclass.order.default=io.quarkus.test.junit.util.QuarkusTestProfileAwareClassOrderer
\ No newline at end of file
diff --git a/test-framework/junit5/src/test/java/io/quarkus/test/junit/util/QuarkusTestProfileAwareClassOrdererTest.java b/test-framework/junit5/src/test/java/io/quarkus/test/junit/util/QuarkusTestProfileAwareClassOrdererTest.java
index bdd49032f378e..25b974da082e8 100644
--- a/test-framework/junit5/src/test/java/io/quarkus/test/junit/util/QuarkusTestProfileAwareClassOrdererTest.java
+++ b/test-framework/junit5/src/test/java/io/quarkus/test/junit/util/QuarkusTestProfileAwareClassOrdererTest.java
@@ -21,6 +21,8 @@
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
+import io.quarkus.test.common.QuarkusTestResource;
+import io.quarkus.test.common.QuarkusTestResourceLifecycleManager;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.junit.QuarkusTestProfile;
import io.quarkus.test.junit.TestProfile;
@@ -35,7 +37,7 @@ class QuarkusTestProfileAwareClassOrdererTest {
@Test
void singleClass() {
- doReturn(Arrays.asList(descriptorMock(Test1.class)))
+ doReturn(Arrays.asList(descriptorMock(Test01.class)))
.when(contextMock).getClassDescriptors();
underTest.orderClasses(contextMock);
@@ -45,20 +47,26 @@ void singleClass() {
@Test
void allVariants() {
- ClassDescriptor quarkusTest1Desc = quarkusDescriptorMock(Test1.class, null);
- ClassDescriptor quarkusTest2Desc = quarkusDescriptorMock(Test2.class, null);
- ClassDescriptor quarkusTestWithProfile1Desc = quarkusDescriptorMock(Test3.class, Profile1.class);
- ClassDescriptor quarkusTestWithProfile2Test4Desc = quarkusDescriptorMock(Test4.class, Profile2.class);
- ClassDescriptor quarkusTestWithProfile2Test5Desc = quarkusDescriptorMock(Test5.class, Profile2.class);
- ClassDescriptor nonQuarkusTest6Desc = descriptorMock(Test6.class);
- ClassDescriptor nonQuarkusTest7Desc = descriptorMock(Test7.class);
+ ClassDescriptor quarkusTest1Desc = quarkusDescriptorMock(Test01.class, null);
+ ClassDescriptor quarkusTestWithUnrestrictedResourceDesc = quarkusDescriptorMock(Test02.class, Manager3.class, false);
+ ClassDescriptor quarkusTest2Desc = quarkusDescriptorMock(Test03.class, null);
+ ClassDescriptor quarkusTestWithProfile1Desc = quarkusDescriptorMock(Test04.class, Profile1.class);
+ ClassDescriptor quarkusTestWithProfile2Test4Desc = quarkusDescriptorMock(Test05.class, Profile2.class);
+ ClassDescriptor quarkusTestWithProfile2Test5Desc = quarkusDescriptorMock(Test06.class, Profile2.class);
+ ClassDescriptor quarkusTestWithRestrictedResourceDesc = quarkusDescriptorMock(Test07.class, Manager2.class, true);
+ ClassDescriptor quarkusTestWithMetaResourceDesc = quarkusDescriptorMock(Test08.class, Manager1.class, false);
+ ClassDescriptor nonQuarkusTest1Desc = descriptorMock(Test09.class);
+ ClassDescriptor nonQuarkusTest2Desc = descriptorMock(Test10.class);
List input = Arrays.asList(
- nonQuarkusTest7Desc,
+ quarkusTestWithRestrictedResourceDesc,
+ nonQuarkusTest2Desc,
quarkusTestWithProfile2Test5Desc,
quarkusTest2Desc,
- nonQuarkusTest6Desc,
+ nonQuarkusTest1Desc,
+ quarkusTestWithMetaResourceDesc,
quarkusTest1Desc,
quarkusTestWithProfile2Test4Desc,
+ quarkusTestWithUnrestrictedResourceDesc,
quarkusTestWithProfile1Desc);
doReturn(input).when(contextMock).getClassDescriptors();
@@ -66,18 +74,21 @@ void allVariants() {
assertThat(input).containsExactly(
quarkusTest1Desc,
+ quarkusTestWithUnrestrictedResourceDesc,
quarkusTest2Desc,
quarkusTestWithProfile1Desc,
quarkusTestWithProfile2Test4Desc,
quarkusTestWithProfile2Test5Desc,
- nonQuarkusTest6Desc,
- nonQuarkusTest7Desc);
+ quarkusTestWithRestrictedResourceDesc,
+ quarkusTestWithMetaResourceDesc,
+ nonQuarkusTest1Desc,
+ nonQuarkusTest2Desc);
}
@Test
void configuredPrefix() {
- ClassDescriptor quarkusTestDesc = quarkusDescriptorMock(Test1.class, null);
- ClassDescriptor nonQuarkusTestDesc = descriptorMock(Test2.class);
+ ClassDescriptor quarkusTestDesc = quarkusDescriptorMock(Test01.class, null);
+ ClassDescriptor nonQuarkusTestDesc = descriptorMock(Test03.class);
List input = Arrays.asList(quarkusTestDesc, nonQuarkusTestDesc);
doReturn(input).when(contextMock).getClassDescriptors();
@@ -92,8 +103,8 @@ void configuredPrefix() {
@Test
void customOrderKey() {
- ClassDescriptor quarkusTest1Desc = quarkusDescriptorMock(Test1.class, null);
- ClassDescriptor quarkusTest2Desc = quarkusDescriptorMock(Test2.class, null);
+ ClassDescriptor quarkusTest1Desc = quarkusDescriptorMock(Test01.class, null);
+ ClassDescriptor quarkusTest2Desc = quarkusDescriptorMock(Test03.class, null);
List input = Arrays.asList(quarkusTest1Desc, quarkusTest2Desc);
doReturn(input).when(contextMock).getClassDescriptors();
@@ -125,30 +136,61 @@ private ClassDescriptor quarkusDescriptorMock(Class> testClass, Class extend
return mock;
}
- private static class Test1 {
- };
+ private ClassDescriptor quarkusDescriptorMock(Class> testClass,
+ Class extends QuarkusTestResourceLifecycleManager> managerClass, boolean restrictToAnnotatedClass) {
+ ClassDescriptor mock = descriptorMock(testClass);
+ when(mock.isAnnotated(QuarkusTest.class)).thenReturn(true);
+ QuarkusTestResource resourceMock = Mockito.mock(QuarkusTestResource.class, withSettings().lenient());
+ doReturn(managerClass).when(resourceMock).value();
+ when(resourceMock.restrictToAnnotatedClass()).thenReturn(restrictToAnnotatedClass);
+ when(mock.findRepeatableAnnotations(QuarkusTestResource.class)).thenReturn(List.of(resourceMock));
+ return mock;
+ }
+
+ private static class Test01 {
+ }
+
+ // this single made-up test class needs an actual annotation since the orderer will have do the meta-check directly because ClassDescriptor does not offer any details whether an annotation is directly annotated or meta-annotated
+ @QuarkusTestResource(Manager3.class)
+ private static class Test02 {
+ }
+
+ private static class Test03 {
+ }
+
+ private static class Test04 {
+ }
- private static class Test2 {
- };
+ private static class Test05 {
+ }
- private static class Test3 {
- };
+ private static class Test06 {
+ }
- private static class Test4 {
- };
+ private static class Test07 {
+ }
- private static class Test5 {
- };
+ private static class Test08 {
+ }
- private static class Test6 {
- };
+ private static class Test09 {
+ }
- private static class Test7 {
- };
+ private static class Test10 {
+ }
private static class Profile1 implements QuarkusTestProfile {
}
private static class Profile2 implements QuarkusTestProfile {
}
+
+ private static interface Manager1 extends QuarkusTestResourceLifecycleManager {
+ }
+
+ private static interface Manager2 extends QuarkusTestResourceLifecycleManager {
+ }
+
+ private static interface Manager3 extends QuarkusTestResourceLifecycleManager {
+ }
}