From 35d76c413674bae47e10b4e6e521cb74310c0c06 Mon Sep 17 00:00:00 2001 From: Stephane Epardaud Date: Tue, 16 Feb 2021 15:58:30 +0100 Subject: [PATCH] TestResourceManager: support test resource annotations on profile --- .../asciidoc/getting-started-testing.adoc | 6 +- .../client/AbsentConfigMapPropertiesPMT.java | 2 +- .../client/ConfigMapPropertiesTest.java | 2 +- .../client/KubernetesClientTest.java | 2 +- .../client/KubernetesNewClientTest.java | 2 +- .../KubernetesTestServerOnProfileTest.java | 72 +++++++++++++++++++ .../NamespacedConfigMapPropertiesTest.java | 2 +- .../client/SecretPropertiesTest.java | 2 +- .../test/common/QuarkusTestResource.java | 6 +- .../test/common/TestResourceManager.java | 28 +++++--- .../test/junit/NativeTestExtension.java | 4 +- .../test/junit/QuarkusTestExtension.java | 5 +- .../client/WithKubernetesTestServer.java | 2 +- 13 files changed, 109 insertions(+), 26 deletions(-) create mode 100644 integration-tests/kubernetes-client/src/test/java/io/quarkus/it/kubernetes/client/KubernetesTestServerOnProfileTest.java diff --git a/docs/src/main/asciidoc/getting-started-testing.adoc b/docs/src/main/asciidoc/getting-started-testing.adoc index e57d59f3e224e..b7aada35bd742 100644 --- a/docs/src/main/asciidoc/getting-started-testing.adoc +++ b/docs/src/main/asciidoc/getting-started-testing.adoc @@ -987,8 +987,8 @@ A very common need is to start some services on which your Quarkus application d By simply annotating any test in the test suite with `@QuarkusTestResource`, Quarkus will run the corresponding `QuarkusTestResourceLifecycleManager` before any tests are run. A test suite is also free to utilize multiple `@QuarkusTestResource` annotations, in which case all the corresponding `QuarkusTestResourceLifecycleManager` objects will be run before the tests. When using multiple test resources they can be started concurrently. For that you need to set `@QuarkusTestResource(parallel = true)`. -NOTE: test resources are global, even if they are defined on a test class, which means they will all be activated for all tests, even though we do -remove duplicates. If you want to only enable a test resource on a single test class, you can use `@QuarkusTestResource(restrictToAnnotatedTest = true)`. +NOTE: test resources are global, even if they are defined on a test class or custom profile, which means they will all be activated for all tests, even though we do +remove duplicates. If you want to only enable a test resource on a single test class or test profile, you can use `@QuarkusTestResource(restrictToAnnotatedClass = true)`. Quarkus provides a few implementations of `QuarkusTestResourceLifecycleManager` out of the box (see `io.quarkus.test.h2.H2DatabaseTestResource` which starts an H2 database, or `io.quarkus.test.kubernetes.client.KubernetesMockServerTestResource` which starts a mock Kubernetes API server), but it is common to create custom implementations to address specific application needs. @@ -1001,7 +1001,7 @@ It is possible to write test resources that are enabled and configured using ann on an annotation which will be used to enable and configure the test resource. For example, this defines the `@WithKubernetesTestServer` annotation, which you can use on your tests to activate the `KubernetesServerTestResource`, -but only for the annotated test class. +but only for the annotated test class. You can also place them on your `QuarkusTestProfile` test profiles. [source,java] ---- diff --git a/integration-tests/kubernetes-client/src/test/java/io/quarkus/it/kubernetes/client/AbsentConfigMapPropertiesPMT.java b/integration-tests/kubernetes-client/src/test/java/io/quarkus/it/kubernetes/client/AbsentConfigMapPropertiesPMT.java index 051b6271f2091..81cd82376b350 100644 --- a/integration-tests/kubernetes-client/src/test/java/io/quarkus/it/kubernetes/client/AbsentConfigMapPropertiesPMT.java +++ b/integration-tests/kubernetes-client/src/test/java/io/quarkus/it/kubernetes/client/AbsentConfigMapPropertiesPMT.java @@ -11,7 +11,7 @@ import io.quarkus.test.QuarkusProdModeTest; import io.quarkus.test.common.QuarkusTestResource; -@QuarkusTestResource(value = CustomKubernetesMockServerTestResource.class, restrictToAnnotatedTest = true) +@QuarkusTestResource(value = CustomKubernetesMockServerTestResource.class, restrictToAnnotatedClass = true) public class AbsentConfigMapPropertiesPMT { @RegisterExtension diff --git a/integration-tests/kubernetes-client/src/test/java/io/quarkus/it/kubernetes/client/ConfigMapPropertiesTest.java b/integration-tests/kubernetes-client/src/test/java/io/quarkus/it/kubernetes/client/ConfigMapPropertiesTest.java index dab151f6c39dc..03d0102941a06 100644 --- a/integration-tests/kubernetes-client/src/test/java/io/quarkus/it/kubernetes/client/ConfigMapPropertiesTest.java +++ b/integration-tests/kubernetes-client/src/test/java/io/quarkus/it/kubernetes/client/ConfigMapPropertiesTest.java @@ -8,7 +8,7 @@ import io.quarkus.test.common.QuarkusTestResource; import io.quarkus.test.junit.QuarkusTest; -@QuarkusTestResource(value = CustomKubernetesMockServerTestResource.class, restrictToAnnotatedTest = true) +@QuarkusTestResource(value = CustomKubernetesMockServerTestResource.class, restrictToAnnotatedClass = true) @QuarkusTest public class ConfigMapPropertiesTest { diff --git a/integration-tests/kubernetes-client/src/test/java/io/quarkus/it/kubernetes/client/KubernetesClientTest.java b/integration-tests/kubernetes-client/src/test/java/io/quarkus/it/kubernetes/client/KubernetesClientTest.java index 6a914246d6e2a..304cc1e5bb779 100644 --- a/integration-tests/kubernetes-client/src/test/java/io/quarkus/it/kubernetes/client/KubernetesClientTest.java +++ b/integration-tests/kubernetes-client/src/test/java/io/quarkus/it/kubernetes/client/KubernetesClientTest.java @@ -19,7 +19,7 @@ * KubernetesClientTest.TestResource contains the entire process of setting up the Mock Kubernetes API Server * It has to live there otherwise the Kubernetes client in native mode won't be able to locate the mock API Server */ -@QuarkusTestResource(value = CustomKubernetesMockServerTestResource.class, restrictToAnnotatedTest = true) +@QuarkusTestResource(value = CustomKubernetesMockServerTestResource.class, restrictToAnnotatedClass = true) @QuarkusTest public class KubernetesClientTest { diff --git a/integration-tests/kubernetes-client/src/test/java/io/quarkus/it/kubernetes/client/KubernetesNewClientTest.java b/integration-tests/kubernetes-client/src/test/java/io/quarkus/it/kubernetes/client/KubernetesNewClientTest.java index dcb11707b963a..2222bbb00a8ed 100644 --- a/integration-tests/kubernetes-client/src/test/java/io/quarkus/it/kubernetes/client/KubernetesNewClientTest.java +++ b/integration-tests/kubernetes-client/src/test/java/io/quarkus/it/kubernetes/client/KubernetesNewClientTest.java @@ -17,7 +17,7 @@ * KubernetesClientTest.TestResource contains the entire process of setting up the Mock Kubernetes API Server * It has to live there otherwise the Kubernetes client in native mode won't be able to locate the mock API Server */ -@QuarkusTestResource(value = CustomKubernetesTestServerTestResource.class, restrictToAnnotatedTest = true) +@QuarkusTestResource(value = CustomKubernetesTestServerTestResource.class, restrictToAnnotatedClass = true) @QuarkusTest public class KubernetesNewClientTest { diff --git a/integration-tests/kubernetes-client/src/test/java/io/quarkus/it/kubernetes/client/KubernetesTestServerOnProfileTest.java b/integration-tests/kubernetes-client/src/test/java/io/quarkus/it/kubernetes/client/KubernetesTestServerOnProfileTest.java new file mode 100644 index 0000000000000..6bb8e6d2c4222 --- /dev/null +++ b/integration-tests/kubernetes-client/src/test/java/io/quarkus/it/kubernetes/client/KubernetesTestServerOnProfileTest.java @@ -0,0 +1,72 @@ +package io.quarkus.it.kubernetes.client; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import io.fabric8.kubernetes.client.server.mock.KubernetesServer; +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.junit.QuarkusTestProfile; +import io.quarkus.test.junit.TestProfile; +import io.quarkus.test.kubernetes.client.KubernetesTestServer; +import io.quarkus.test.kubernetes.client.WithKubernetesTestServer; + +/* + * This class has no native-image test because it relies on setting config overrides that clash + * with native image build config. + * This is the same test as KubernetesTestServerTest but with the test resource annotation on the profile + */ +@TestProfile(KubernetesTestServerOnProfileTest.MyProfile.class) +@QuarkusTest +public class KubernetesTestServerOnProfileTest { + + private static KubernetesServer setupServer; + + public static class Setup implements Consumer { + + @Override + public void accept(KubernetesServer t) { + setupServer = t; + } + + } + + @KubernetesTestServer + private KubernetesServer mockServer; + + @Test + public void testConfiguration() throws InterruptedException { + // we can't really test CRUD, and HTTPS doesn't work + Assertions.assertEquals(10001, mockServer.getMockServer().getPort()); + Assertions.assertSame(mockServer, setupServer); + } + + @WithKubernetesTestServer(https = false, crud = true, port = 10001, setup = KubernetesTestServerOnProfileTest.Setup.class) + public static class MyProfile implements QuarkusTestProfile { + + @Override + public Map getConfigOverrides() { + Map overrides = new HashMap<>(); + // do not fetch config from kubernetes + overrides.put("quarkus.kubernetes-config.enabled", "false"); + overrides.put("quarkus.kubernetes-config.secrets.enabled", "false"); + // get rid of errors due to us not populating config from kubernetes + overrides.put("dummy", "asd"); + overrides.put("some.prop1", "asd"); + overrides.put("some.prop2", "asd"); + overrides.put("some.prop3", "asd"); + overrides.put("some.prop4", "asd"); + overrides.put("some.prop5", "asd"); + overrides.put("secret.prop1", "asd"); + overrides.put("secret.prop2", "asd"); + overrides.put("secret.prop3", "asd"); + overrides.put("secret.prop4", "asd"); + overrides.put("overridden.secret", "asd"); + overrides.put("dummysecret", "asd"); + return overrides; + } + } +} diff --git a/integration-tests/kubernetes-client/src/test/java/io/quarkus/it/kubernetes/client/NamespacedConfigMapPropertiesTest.java b/integration-tests/kubernetes-client/src/test/java/io/quarkus/it/kubernetes/client/NamespacedConfigMapPropertiesTest.java index b56143e148925..ea203c1c90b15 100644 --- a/integration-tests/kubernetes-client/src/test/java/io/quarkus/it/kubernetes/client/NamespacedConfigMapPropertiesTest.java +++ b/integration-tests/kubernetes-client/src/test/java/io/quarkus/it/kubernetes/client/NamespacedConfigMapPropertiesTest.java @@ -10,7 +10,7 @@ import io.quarkus.test.junit.QuarkusTestProfile; import io.quarkus.test.junit.TestProfile; -@QuarkusTestResource(value = CustomKubernetesMockServerTestResource.class, restrictToAnnotatedTest = true) +@QuarkusTestResource(value = CustomKubernetesMockServerTestResource.class, restrictToAnnotatedClass = true) @TestProfile(NamespacedConfigMapPropertiesTest.MyProfile.class) @QuarkusTest public class NamespacedConfigMapPropertiesTest { diff --git a/integration-tests/kubernetes-client/src/test/java/io/quarkus/it/kubernetes/client/SecretPropertiesTest.java b/integration-tests/kubernetes-client/src/test/java/io/quarkus/it/kubernetes/client/SecretPropertiesTest.java index 06205e2f740c1..425fef9d150f7 100644 --- a/integration-tests/kubernetes-client/src/test/java/io/quarkus/it/kubernetes/client/SecretPropertiesTest.java +++ b/integration-tests/kubernetes-client/src/test/java/io/quarkus/it/kubernetes/client/SecretPropertiesTest.java @@ -8,7 +8,7 @@ import io.quarkus.test.common.QuarkusTestResource; import io.quarkus.test.junit.QuarkusTest; -@QuarkusTestResource(value = CustomKubernetesMockServerTestResource.class, restrictToAnnotatedTest = true) +@QuarkusTestResource(value = CustomKubernetesMockServerTestResource.class, restrictToAnnotatedClass = true) @QuarkusTest public class SecretPropertiesTest { diff --git a/test-framework/common/src/main/java/io/quarkus/test/common/QuarkusTestResource.java b/test-framework/common/src/main/java/io/quarkus/test/common/QuarkusTestResource.java index a356848b26d83..a439aff41d39b 100644 --- a/test-framework/common/src/main/java/io/quarkus/test/common/QuarkusTestResource.java +++ b/test-framework/common/src/main/java/io/quarkus/test/common/QuarkusTestResource.java @@ -43,11 +43,11 @@ boolean parallel() default false; /** - * Whether this annotation should only be enabled if it is placed on the currently running test class. + * Whether this annotation should only be enabled if it is placed on the currently running test class or test profile. * Note that this defaults to true for meta-annotations since meta-annotations are only considered - * for the current test class. + * for the current test class or test profile. */ - boolean restrictToAnnotatedTest() default false; + boolean restrictToAnnotatedClass() default false; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) diff --git a/test-framework/common/src/main/java/io/quarkus/test/common/TestResourceManager.java b/test-framework/common/src/main/java/io/quarkus/test/common/TestResourceManager.java index 93866e1a989a2..3455b05802ba1 100644 --- a/test-framework/common/src/main/java/io/quarkus/test/common/TestResourceManager.java +++ b/test-framework/common/src/main/java/io/quarkus/test/common/TestResourceManager.java @@ -30,8 +30,6 @@ public class TestResourceManager implements Closeable { - private static final DotName DOTNAME_QUARKUS_TEST_RESOURCE = DotName.createSimple(QuarkusTestResource.class.getName()); - private final List sequentialTestResourceEntries; private final List parallelTestResourceEntries; private final List allTestResourceEntries; @@ -40,10 +38,10 @@ public class TestResourceManager implements Closeable { private boolean hasPerTestResources = false; public TestResourceManager(Class testClass) { - this(testClass, Collections.emptyList(), false); + this(testClass, null, Collections.emptyList(), false); } - public TestResourceManager(Class testClass, List additionalTestResources, + public TestResourceManager(Class testClass, Class profileClass, List additionalTestResources, boolean disableGlobalTestResources) { this.parallelTestResourceEntries = new ArrayList<>(); this.sequentialTestResourceEntries = new ArrayList<>(); @@ -54,7 +52,7 @@ public TestResourceManager(Class testClass, List addi if (disableGlobalTestResources) { uniqueEntries = new HashSet<>(additionalTestResources); } else { - uniqueEntries = getUniqueTestResourceClassEntries(testClass, additionalTestResources); + uniqueEntries = getUniqueTestResourceClassEntries(testClass, profileClass, additionalTestResources); } Set remainingUniqueEntries = initParallelTestResources(uniqueEntries); initSequentialTestResources(remainingUniqueEntries); @@ -250,20 +248,30 @@ private TestResourceManager.TestResourceEntry buildTestResourceEntry(TestResourc } } - private Set getUniqueTestResourceClassEntries(Class testClass, + private Set getUniqueTestResourceClassEntries(Class testClass, Class profileClass, List additionalTestResources) { IndexView index = TestClassIndexer.readIndex(testClass); Set uniqueEntries = new HashSet<>(); - // reload the test class in the right CL + // reload the test and profile classes in the right CL Class testClassFromTCCL; + Class profileClassFromTCCL; try { testClassFromTCCL = Class.forName(testClass.getName(), false, Thread.currentThread().getContextClassLoader()); + if (profileClass != null) { + profileClassFromTCCL = Class.forName(profileClass.getName(), false, + Thread.currentThread().getContextClassLoader()); + } else { + profileClassFromTCCL = null; + } } catch (ClassNotFoundException e) { throw new RuntimeException(e); } // 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); + if (profileClassFromTCCL != null) { + collectMetaAnnotations(profileClassFromTCCL, uniqueEntries); + } for (AnnotationInstance annotation : findQuarkusTestResourceInstances(testClass, index)) { try { Class testResourceClass = loadTestResourceClassFromTCCL( @@ -287,7 +295,7 @@ private Set getUniqueTestResourceClassEntries(Class t isParallel = parallelAnnotationValue.asBoolean(); } - AnnotationValue restrict = annotation.value("restrictToAnnotatedTest"); + AnnotationValue restrict = annotation.value("restrictToAnnotatedClass"); if (restrict != null && restrict.asBoolean()) { hasPerTestResources = true; } @@ -309,7 +317,7 @@ private void collectMetaAnnotations(Class testClassFromTCCL, Set testResourceClass = testResource.value(); @@ -379,7 +387,7 @@ public boolean hasPerTestResources() { } private boolean keepTestResourceAnnotation(AnnotationInstance annotation, ClassInfo targetClass, Set testClasses) { - AnnotationValue restrict = annotation.value("restrictToAnnotatedTest"); + AnnotationValue restrict = annotation.value("restrictToAnnotatedClass"); if (restrict != null && restrict.asBoolean()) { return testClasses.contains(targetClass.name().toString('.')); } diff --git a/test-framework/junit5/src/main/java/io/quarkus/test/junit/NativeTestExtension.java b/test-framework/junit5/src/main/java/io/quarkus/test/junit/NativeTestExtension.java index bbb4a4f0a091a..9509bbeb0f5e2 100644 --- a/test-framework/junit5/src/main/java/io/quarkus/test/junit/NativeTestExtension.java +++ b/test-framework/junit5/src/main/java/io/quarkus/test/junit/NativeTestExtension.java @@ -3,6 +3,7 @@ import java.io.Closeable; import java.io.IOException; import java.lang.reflect.Field; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -173,7 +174,8 @@ private ExtensionState doNativeStart(ExtensionContext context, Class requiredTestClass) { while (requiredTestClass != Object.class) { for (QuarkusTestResource testResource : requiredTestClass.getAnnotationsByType(QuarkusTestResource.class)) { - if (testResource.restrictToAnnotatedTest()) { + if (testResource.restrictToAnnotatedClass()) { return true; } } diff --git a/test-framework/kubernetes-client/src/main/java/io/quarkus/test/kubernetes/client/WithKubernetesTestServer.java b/test-framework/kubernetes-client/src/main/java/io/quarkus/test/kubernetes/client/WithKubernetesTestServer.java index 5dfbb472ec61d..c0bf14f1cebec 100644 --- a/test-framework/kubernetes-client/src/main/java/io/quarkus/test/kubernetes/client/WithKubernetesTestServer.java +++ b/test-framework/kubernetes-client/src/main/java/io/quarkus/test/kubernetes/client/WithKubernetesTestServer.java @@ -13,7 +13,7 @@ * Use on your test resource to get a mock {@link KubernetesServer} spawn up, and injectable with {@link KubernetesTestServer}. * This annotation is only active when used on a test class, and only for this test class. */ -@QuarkusTestResource(value = KubernetesServerTestResource.class, restrictToAnnotatedTest = true) +@QuarkusTestResource(value = KubernetesServerTestResource.class, restrictToAnnotatedClass = true) @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface WithKubernetesTestServer {