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

QuarkusTest: veto all test classes belonging to a different test profile #45336

Merged
merged 1 commit into from
Jan 3, 2025
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 @@ -6,12 +6,15 @@
import java.util.Optional;
import java.util.Set;

import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.AnnotationTarget;
import org.jboss.jandex.AnnotationTarget.Kind;
import org.jboss.jandex.AnnotationTransformation;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.ClassInfo.NestingType;
import org.jboss.jandex.DotName;
import org.jboss.jandex.IndexView;
import org.jboss.logging.Logger;

import io.quarkus.arc.processor.Annotations;
import io.quarkus.arc.processor.AnnotationsTransformer;
Expand All @@ -26,6 +29,16 @@

public class TestsAsBeansProcessor {

private static final Logger LOG = Logger.getLogger(TestsAsBeansProcessor.class);

private static final DotName QUARKUS_TEST_PROFILE = DotName.createSimple("io.quarkus.test.junit.QuarkusTestProfile");
private static final DotName TEST_PROFILE = DotName.createSimple("io.quarkus.test.junit.TestProfile");
private static final DotName QUARKUS_TEST = DotName.createSimple("io.quarkus.test.junit.QuarkusTest");
private static final DotName QUARKUS_INTEGRATION_TEST = DotName
.createSimple("io.quarkus.test.junit.QuarkusIntegrationTest");
private static final DotName QUARKUS_MAIN_TEST = DotName.createSimple("io.quarkus.test.junit.main.QuarkusMainTest");
private static final DotName NESTED = DotName.createSimple("org.junit.jupiter.api.Nested");

@BuildStep
public void testAnnotations(List<TestAnnotationBuildItem> items, BuildProducer<BeanDefiningAnnotationBuildItem> producer) {
for (TestAnnotationBuildItem item : items) {
Expand All @@ -46,6 +59,37 @@ public void testClassBeans(List<TestClassBeanBuildItem> items, BuildProducer<Add
producer.produce(builder.build());
}

@BuildStep(onlyIf = IsTest.class)
AnnotationsTransformerBuildItem vetoTestClassesNotMatchingTestProfile(Optional<TestProfileBuildItem> testProfile,
CombinedIndexBuildItem index) {
// Since we only need to register all test classes that belong to the "current" test profile
// we need to veto other test classes during the build
return new AnnotationsTransformerBuildItem(AnnotationTransformation
.forClasses()
.when(tc -> {
ClassInfo maybeTestClass = tc.declaration().asClass();
boolean veto = false;
if (maybeTestClass.hasAnnotation(QUARKUS_TEST)
|| maybeTestClass.hasAnnotation(QUARKUS_INTEGRATION_TEST)
|| maybeTestClass.hasAnnotation(QUARKUS_MAIN_TEST)) {
String testProfileClassName = testProfile.map(TestProfileBuildItem::getTestProfileClassName)
.orElse(null);
veto = !matchesProfile(maybeTestClass, testProfileClassName);
if (veto && hasMatchingNestedTest(maybeTestClass, testProfileClassName, index.getComputingIndex())) {
// FIXME the current @Nested tests support makes it possible to specify a different test profile
// which is wrong and may cause troubles if a test class injects beans enabled/disabled in a specific profile
// See https://github.com/quarkusio/quarkus/issues/45349
LOG.warnf(
"Test class [%s] does not match the current test profile [%s] but cannot be vetoed because it declares a matching @Nested test",
maybeTestClass, testProfileClassName);
veto = false;
}
}
return veto;
})
.transform(tc -> tc.add(AnnotationInstance.builder(DotNames.VETOED).buildWithTarget(tc.declaration()))));
mkouba marked this conversation as resolved.
Show resolved Hide resolved
}

@BuildStep(onlyIf = IsTest.class)
AnnotationsTransformerBuildItem vetoTestProfileBeans(Optional<TestProfileBuildItem> testProfile,
CustomScopeAnnotationsBuildItem customScopes, CombinedIndexBuildItem index) {
Expand Down Expand Up @@ -89,8 +133,6 @@ && isTestProfileClass(declaringClass, index.getComputingIndex())
});
}

private static final DotName QUARKUS_TEST_PROFILE = DotName.createSimple("io.quarkus.test.junit.QuarkusTestProfile");

private static Set<DotName> initTestProfileHierarchy(Optional<TestProfileBuildItem> testProfile, IndexView index) {
Set<DotName> ret = Set.of();
if (testProfile.isPresent()) {
Expand Down Expand Up @@ -134,4 +176,30 @@ private static boolean isTestProfileClass(ClassInfo clazz, IndexView index) {
return false;
}

private boolean hasMatchingNestedTest(ClassInfo testClass, String testProfileClassName, IndexView index) {
for (DotName memberClassName : testClass.memberClasses()) {
ClassInfo memberClass = index.getClassByName(memberClassName);
if (memberClass != null && memberClass.hasDeclaredAnnotation(NESTED)) {
if (matchesProfile(memberClass, testProfileClassName)
|| hasMatchingNestedTest(memberClass, testProfileClassName, index)) {
return true;
}
}
}
return false;
}

private boolean matchesProfile(ClassInfo testClass, String testProfileClassName) {
AnnotationInstance testProfileAnnotation = testClass.declaredAnnotation(TEST_PROFILE);
if (testProfileClassName == null) {
// No test profile set - match test classes without @TestProfile
return testProfileAnnotation == null;
} else if (testProfileAnnotation != null) {
// Test profile set - match test classes with the same test profile
String annotationProfileClassName = testProfileAnnotation.value().asClass().name().toString();
return testProfileClassName.equals(annotationProfileClassName);
}
return false;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.Collections;
import java.util.List;
Expand All @@ -18,6 +19,7 @@
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import io.quarkus.arc.Arc;
import io.quarkus.it.rest.ExternalService;
import io.quarkus.it.rest.GreetingService;
import io.quarkus.test.common.QuarkusTestResourceLifecycleManager;
Expand Down Expand Up @@ -68,6 +70,7 @@ public void testContext() {
public void testProfileBeans() {
assertEquals("Bonjour Foo", greetingService.greet("Foo"));
assertEquals("profile", externalService.service());
assertTrue(Arc.container().select(SharedNormalTestCase.class).isUnsatisfied());
}

public static class MyProfile implements QuarkusTestProfile {
Expand Down
Loading