Skip to content

Commit

Permalink
Merge pull request quarkusio#45336 from mkouba/issue-45308
Browse files Browse the repository at this point in the history
QuarkusTest: veto all test classes belonging to a different test profile
  • Loading branch information
mkouba authored Jan 3, 2025
2 parents 39c5261 + 7123aeb commit 9e2523f
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 2 deletions.
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()))));
}

@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

0 comments on commit 9e2523f

Please sign in to comment.