From f34fa1a59fa464a0ca545ce0537f1b99e0ab9041 Mon Sep 17 00:00:00 2001 From: Martin Kouba Date: Mon, 8 Jul 2024 11:28:51 +0200 Subject: [PATCH 1/2] QuarkusComponentTest: register default and support custom converters - fixes #41709 --- .../test/component/QuarkusComponentTest.java | 8 +++ .../QuarkusComponentTestConfiguration.java | 49 +++++++++++++++-- .../QuarkusComponentTestExtension.java | 4 +- .../QuarkusComponentTestExtensionBuilder.java | 28 +++++++++- .../config/ConfigConverterExtensionTest.java | 55 +++++++++++++++++++ .../component/config/ConfigConverterTest.java | 52 ++++++++++++++++++ 6 files changed, 187 insertions(+), 9 deletions(-) create mode 100644 test-framework/junit5-component/src/test/java/io/quarkus/test/component/config/ConfigConverterExtensionTest.java create mode 100644 test-framework/junit5-component/src/test/java/io/quarkus/test/component/config/ConfigConverterTest.java diff --git a/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTest.java b/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTest.java index 3d66a0e200756..3af12981b584e 100644 --- a/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTest.java +++ b/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTest.java @@ -6,6 +6,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.Target; +import org.eclipse.microprofile.config.spi.Converter; import org.junit.jupiter.api.extension.ExtendWith; import io.quarkus.arc.processor.AnnotationsTransformer; @@ -72,4 +73,11 @@ */ Class[] annotationsTransformers() default {}; + /** + * The additional config converters. By default, the Quarkus-specific converters are registered. + * + * @see QuarkusComponentTestExtensionBuilder#addConverter(Converter) + */ + Class>[] configConverters() default {}; + } diff --git a/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestConfiguration.java b/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestConfiguration.java index 8279bbf99c84b..8bde3f4282314 100644 --- a/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestConfiguration.java +++ b/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestConfiguration.java @@ -17,16 +17,42 @@ import jakarta.inject.Inject; import jakarta.inject.Provider; +import org.eclipse.microprofile.config.spi.Converter; import org.jboss.logging.Logger; import io.quarkus.arc.InjectableInstance; import io.quarkus.arc.processor.AnnotationsTransformer; +import io.quarkus.runtime.configuration.CharsetConverter; +import io.quarkus.runtime.configuration.CidrAddressConverter; +import io.quarkus.runtime.configuration.DurationConverter; +import io.quarkus.runtime.configuration.InetAddressConverter; +import io.quarkus.runtime.configuration.InetSocketAddressConverter; +import io.quarkus.runtime.configuration.LocaleConverter; +import io.quarkus.runtime.configuration.MemorySizeConverter; +import io.quarkus.runtime.configuration.PathConverter; +import io.quarkus.runtime.configuration.RegexConverter; +import io.quarkus.runtime.configuration.ZoneIdConverter; +import io.quarkus.runtime.logging.LevelConverter; import io.quarkus.test.InjectMock; class QuarkusComponentTestConfiguration { + // As defined in /quarkus/core/runtime/src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.Converter + static final List> DEFAULT_CONVERTERS = List.of(new InetSocketAddressConverter(), + new CharsetConverter(), + new CidrAddressConverter(), + new InetAddressConverter(), + new RegexConverter(), + new PathConverter(), + new DurationConverter(), + new MemorySizeConverter(), + new LocaleConverter(), + new ZoneIdConverter(), + new LevelConverter()); + static final QuarkusComponentTestConfiguration DEFAULT = new QuarkusComponentTestConfiguration(Map.of(), List.of(), - List.of(), false, true, QuarkusComponentTestExtensionBuilder.DEFAULT_CONFIG_SOURCE_ORDINAL, List.of()); + List.of(), false, true, QuarkusComponentTestExtensionBuilder.DEFAULT_CONFIG_SOURCE_ORDINAL, List.of(), + DEFAULT_CONVERTERS); private static final Logger LOG = Logger.getLogger(QuarkusComponentTestConfiguration.class); @@ -37,11 +63,12 @@ class QuarkusComponentTestConfiguration { final boolean addNestedClassesAsComponents; final int configSourceOrdinal; final List annotationsTransformers; + final List> configConverters; QuarkusComponentTestConfiguration(Map configProperties, List> componentClasses, List> mockConfigurators, boolean useDefaultConfigProperties, boolean addNestedClassesAsComponents, int configSourceOrdinal, - List annotationsTransformers) { + List annotationsTransformers, List> configConverters) { this.configProperties = configProperties; this.componentClasses = componentClasses; this.mockConfigurators = mockConfigurators; @@ -49,6 +76,7 @@ class QuarkusComponentTestConfiguration { this.addNestedClassesAsComponents = addNestedClassesAsComponents; this.configSourceOrdinal = configSourceOrdinal; this.annotationsTransformers = annotationsTransformers; + this.configConverters = configConverters; } QuarkusComponentTestConfiguration update(Class testClass) { @@ -58,6 +86,7 @@ QuarkusComponentTestConfiguration update(Class testClass) { boolean addNestedClassesAsComponents = this.addNestedClassesAsComponents; int configSourceOrdinal = this.configSourceOrdinal; List annotationsTransformers = new ArrayList<>(this.annotationsTransformers); + List> configConverters = new ArrayList<>(this.configConverters); QuarkusComponentTest testAnnotation = testClass.getAnnotation(QuarkusComponentTest.class); if (testAnnotation != null) { @@ -71,7 +100,17 @@ QuarkusComponentTestConfiguration update(Class testClass) { try { annotationsTransformers.add(transformerClass.getDeclaredConstructor().newInstance()); } catch (Exception e) { - LOG.errorf("Unable to instantiate %s", transformerClass); + LOG.errorf(e, "Unable to instantiate %s", transformerClass); + } + } + } + Class>[] converters = testAnnotation.configConverters(); + if (converters.length > 0) { + for (Class> converterClass : converters) { + try { + configConverters.add(converterClass.getDeclaredConstructor().newInstance()); + } catch (Exception e) { + LOG.errorf(e, "Unable to instantiate %s", converterClass); } } } @@ -120,7 +159,7 @@ QuarkusComponentTestConfiguration update(Class testClass) { return new QuarkusComponentTestConfiguration(Map.copyOf(configProperties), List.copyOf(componentClasses), this.mockConfigurators, useDefaultConfigProperties, addNestedClassesAsComponents, configSourceOrdinal, - List.copyOf(annotationsTransformers)); + List.copyOf(annotationsTransformers), List.copyOf(configConverters)); } QuarkusComponentTestConfiguration update(Method testMethod) { @@ -132,7 +171,7 @@ QuarkusComponentTestConfiguration update(Method testMethod) { } return new QuarkusComponentTestConfiguration(configProperties, componentClasses, mockConfigurators, useDefaultConfigProperties, addNestedClassesAsComponents, configSourceOrdinal, - annotationsTransformers); + annotationsTransformers, configConverters); } private static boolean resolvesToBuiltinBean(Class rawType) { diff --git a/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestExtension.java b/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestExtension.java index 2656adaa4fcb1..e8204f6d3f4ec 100644 --- a/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestExtension.java +++ b/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestExtension.java @@ -57,6 +57,7 @@ import org.eclipse.microprofile.config.Config; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.eclipse.microprofile.config.spi.ConfigProviderResolver; +import org.eclipse.microprofile.config.spi.Converter; import org.jboss.jandex.AnnotationInstance; import org.jboss.jandex.AnnotationValue; import org.jboss.jandex.ClassInfo; @@ -203,7 +204,7 @@ public QuarkusComponentTestExtension() { public QuarkusComponentTestExtension(Class... additionalComponentClasses) { this(new QuarkusComponentTestConfiguration(Map.of(), List.of(additionalComponentClasses), List.of(), false, true, QuarkusComponentTestExtensionBuilder.DEFAULT_CONFIG_SOURCE_ORDINAL, - List.of())); + List.of(), List.of())); } QuarkusComponentTestExtension(QuarkusComponentTestConfiguration baseConfiguration) { @@ -421,6 +422,7 @@ private void startContainer(ExtensionContext context, Lifecycle testInstanceLife ClassLoader tccl = Thread.currentThread().getContextClassLoader(); SmallRyeConfigBuilder configBuilder = new SmallRyeConfigBuilder().forClassLoader(tccl) .addDefaultInterceptors() + .withConverters(configuration.configConverters.toArray(new Converter[] {})) .addDefaultSources() .withSources(new ApplicationPropertiesConfigSourceLoader.InFileSystem()) .withSources(new ApplicationPropertiesConfigSourceLoader.InClassPath()) diff --git a/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestExtensionBuilder.java b/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestExtensionBuilder.java index e0c267e507065..cdf13d9c25c3a 100644 --- a/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestExtensionBuilder.java +++ b/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestExtensionBuilder.java @@ -7,6 +7,8 @@ import java.util.Map; import java.util.function.Function; +import org.eclipse.microprofile.config.spi.Converter; + import io.quarkus.arc.processor.AnnotationsTransformer; /** @@ -26,6 +28,7 @@ public class QuarkusComponentTestExtensionBuilder { private final List> componentClasses = new ArrayList<>(); private final List> mockConfigurators = new ArrayList<>(); private final List annotationsTransformers = new ArrayList<>(); + private final List> configConverters = new ArrayList<>(); private boolean useDefaultConfigProperties = false; private boolean addNestedClassesAsComponents = true; private int configSourceOrdinal = QuarkusComponentTestExtensionBuilder.DEFAULT_CONFIG_SOURCE_ORDINAL; @@ -105,6 +108,17 @@ public QuarkusComponentTestExtensionBuilder addAnnotationsTransformer(Annotation return this; } + /** + * Add an additional {@link Converter}. By default, the Quarkus-specific converters are registered. + * + * @param transformer + * @return self + */ + public QuarkusComponentTestExtensionBuilder addConverter(Converter converter) { + configConverters.add(converter); + return this; + } + /** * Configure a new mock of a bean. *

@@ -124,10 +138,18 @@ public MockBeanConfigurator mock(Class beanClass) { * @return a new extension instance */ public QuarkusComponentTestExtension build() { + List> converters; + if (configConverters.isEmpty()) { + converters = QuarkusComponentTestConfiguration.DEFAULT_CONVERTERS; + } else { + converters = new ArrayList<>(QuarkusComponentTestConfiguration.DEFAULT_CONVERTERS); + converters.addAll(configConverters); + converters = List.copyOf(converters); + } return new QuarkusComponentTestExtension(new QuarkusComponentTestConfiguration(Map.copyOf(configProperties), - List.copyOf(componentClasses), - List.copyOf(mockConfigurators), useDefaultConfigProperties, addNestedClassesAsComponents, configSourceOrdinal, - List.copyOf(annotationsTransformers))); + List.copyOf(componentClasses), List.copyOf(mockConfigurators), useDefaultConfigProperties, + addNestedClassesAsComponents, configSourceOrdinal, + List.copyOf(annotationsTransformers), converters)); } void registerMockBean(MockBeanConfiguratorImpl mock) { diff --git a/test-framework/junit5-component/src/test/java/io/quarkus/test/component/config/ConfigConverterExtensionTest.java b/test-framework/junit5-component/src/test/java/io/quarkus/test/component/config/ConfigConverterExtensionTest.java new file mode 100644 index 0000000000000..2714e2ab0a135 --- /dev/null +++ b/test-framework/junit5-component/src/test/java/io/quarkus/test/component/config/ConfigConverterExtensionTest.java @@ -0,0 +1,55 @@ +package io.quarkus.test.component.config; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.time.Duration; +import java.util.concurrent.TimeUnit; + +import jakarta.annotation.Priority; +import jakarta.inject.Singleton; + +import org.eclipse.microprofile.config.inject.ConfigProperty; +import org.eclipse.microprofile.config.spi.Converter; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.component.QuarkusComponentTestExtension; +import io.quarkus.test.component.TestConfigProperty; + +public class ConfigConverterExtensionTest { + + @RegisterExtension + static final QuarkusComponentTestExtension extension = QuarkusComponentTestExtension.builder() + .addConverter(new CustomBooleanConverter()).build(); + + @TestConfigProperty(key = "my.boolean", value = "jo") + @TestConfigProperty(key = "my.duration", value = "5s") + @Test + public void testQuarkusDurationConverter(Foo foo) { + assertEquals(TimeUnit.SECONDS.toMillis(5), foo.durationVal.toMillis()); + assertTrue(foo.boolVal); + } + + @Singleton + public static class Foo { + + @ConfigProperty(name = "my.duration", defaultValue = "60s") + Duration durationVal; + + @ConfigProperty(name = "my.boolean") + boolean boolVal; + + } + + @SuppressWarnings("serial") + @Priority(300) + public static class CustomBooleanConverter implements Converter { + + @Override + public Boolean convert(String value) throws IllegalArgumentException, NullPointerException { + return "jo".equals(value) ? true : false; + } + + } +} diff --git a/test-framework/junit5-component/src/test/java/io/quarkus/test/component/config/ConfigConverterTest.java b/test-framework/junit5-component/src/test/java/io/quarkus/test/component/config/ConfigConverterTest.java new file mode 100644 index 0000000000000..28c498b785beb --- /dev/null +++ b/test-framework/junit5-component/src/test/java/io/quarkus/test/component/config/ConfigConverterTest.java @@ -0,0 +1,52 @@ +package io.quarkus.test.component.config; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.time.Duration; +import java.util.concurrent.TimeUnit; + +import jakarta.annotation.Priority; +import jakarta.inject.Singleton; + +import org.eclipse.microprofile.config.inject.ConfigProperty; +import org.eclipse.microprofile.config.spi.Converter; +import org.junit.jupiter.api.Test; + +import io.quarkus.test.component.QuarkusComponentTest; +import io.quarkus.test.component.TestConfigProperty; +import io.quarkus.test.component.config.ConfigConverterTest.CustomBooleanConverter; + +@QuarkusComponentTest(configConverters = CustomBooleanConverter.class) +public class ConfigConverterTest { + + @TestConfigProperty(key = "my.boolean", value = "jo") + @TestConfigProperty(key = "my.duration", value = "5s") + @Test + public void testQuarkusDurationConverter(Foo foo) { + assertEquals(TimeUnit.SECONDS.toMillis(5), foo.durationVal.toMillis()); + assertTrue(foo.boolVal); + } + + @Singleton + public static class Foo { + + @ConfigProperty(name = "my.duration", defaultValue = "60s") + Duration durationVal; + + @ConfigProperty(name = "my.boolean") + boolean boolVal; + + } + + @SuppressWarnings("serial") + @Priority(300) + public static class CustomBooleanConverter implements Converter { + + @Override + public Boolean convert(String value) throws IllegalArgumentException, NullPointerException { + return "jo".equals(value) ? true : false; + } + + } +} From 947bbdb6c0eeabbf3202fd2dd0e79f7c4573dc45 Mon Sep 17 00:00:00 2001 From: Martin Kouba Date: Mon, 8 Jul 2024 16:17:11 +0200 Subject: [PATCH 2/2] Add QuarkusComponentTestExtensionBuilder.setConfigBuilderCustomizer() --- .../QuarkusComponentTestConfiguration.java | 13 +++-- .../QuarkusComponentTestExtension.java | 56 ++++++++++--------- .../QuarkusComponentTestExtensionBuilder.java | 21 ++++++- .../config/ConfigBuilderCustomizerTest.java | 52 +++++++++++++++++ .../config/ConfigConverterExtensionTest.java | 2 +- .../component/config/ConfigConverterTest.java | 2 +- 6 files changed, 113 insertions(+), 33 deletions(-) create mode 100644 test-framework/junit5-component/src/test/java/io/quarkus/test/component/config/ConfigBuilderCustomizerTest.java diff --git a/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestConfiguration.java b/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestConfiguration.java index 8bde3f4282314..1d6f226a713db 100644 --- a/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestConfiguration.java +++ b/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestConfiguration.java @@ -9,6 +9,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.function.Consumer; import jakarta.enterprise.event.Event; import jakarta.enterprise.inject.Instance; @@ -34,6 +35,7 @@ import io.quarkus.runtime.configuration.ZoneIdConverter; import io.quarkus.runtime.logging.LevelConverter; import io.quarkus.test.InjectMock; +import io.smallrye.config.SmallRyeConfigBuilder; class QuarkusComponentTestConfiguration { @@ -52,7 +54,7 @@ class QuarkusComponentTestConfiguration { static final QuarkusComponentTestConfiguration DEFAULT = new QuarkusComponentTestConfiguration(Map.of(), List.of(), List.of(), false, true, QuarkusComponentTestExtensionBuilder.DEFAULT_CONFIG_SOURCE_ORDINAL, List.of(), - DEFAULT_CONVERTERS); + DEFAULT_CONVERTERS, null); private static final Logger LOG = Logger.getLogger(QuarkusComponentTestConfiguration.class); @@ -64,11 +66,13 @@ class QuarkusComponentTestConfiguration { final int configSourceOrdinal; final List annotationsTransformers; final List> configConverters; + final Consumer configBuilderCustomizer; QuarkusComponentTestConfiguration(Map configProperties, List> componentClasses, List> mockConfigurators, boolean useDefaultConfigProperties, boolean addNestedClassesAsComponents, int configSourceOrdinal, - List annotationsTransformers, List> configConverters) { + List annotationsTransformers, List> configConverters, + Consumer configBuilderCustomizer) { this.configProperties = configProperties; this.componentClasses = componentClasses; this.mockConfigurators = mockConfigurators; @@ -77,6 +81,7 @@ class QuarkusComponentTestConfiguration { this.configSourceOrdinal = configSourceOrdinal; this.annotationsTransformers = annotationsTransformers; this.configConverters = configConverters; + this.configBuilderCustomizer = configBuilderCustomizer; } QuarkusComponentTestConfiguration update(Class testClass) { @@ -159,7 +164,7 @@ QuarkusComponentTestConfiguration update(Class testClass) { return new QuarkusComponentTestConfiguration(Map.copyOf(configProperties), List.copyOf(componentClasses), this.mockConfigurators, useDefaultConfigProperties, addNestedClassesAsComponents, configSourceOrdinal, - List.copyOf(annotationsTransformers), List.copyOf(configConverters)); + List.copyOf(annotationsTransformers), List.copyOf(configConverters), configBuilderCustomizer); } QuarkusComponentTestConfiguration update(Method testMethod) { @@ -171,7 +176,7 @@ QuarkusComponentTestConfiguration update(Method testMethod) { } return new QuarkusComponentTestConfiguration(configProperties, componentClasses, mockConfigurators, useDefaultConfigProperties, addNestedClassesAsComponents, configSourceOrdinal, - annotationsTransformers, configConverters); + annotationsTransformers, configConverters, configBuilderCustomizer); } private static boolean resolvesToBuiltinBean(Class rawType) { diff --git a/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestExtension.java b/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestExtension.java index e8204f6d3f4ec..5896ee4f04115 100644 --- a/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestExtension.java +++ b/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestExtension.java @@ -79,6 +79,7 @@ import org.junit.jupiter.api.extension.BeforeAllCallback; import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ExtensionContext.Store; import org.junit.jupiter.api.extension.ParameterContext; import org.junit.jupiter.api.extension.ParameterResolutionException; import org.junit.jupiter.api.extension.ParameterResolver; @@ -204,7 +205,7 @@ public QuarkusComponentTestExtension() { public QuarkusComponentTestExtension(Class... additionalComponentClasses) { this(new QuarkusComponentTestConfiguration(Map.of(), List.of(additionalComponentClasses), List.of(), false, true, QuarkusComponentTestExtensionBuilder.DEFAULT_CONFIG_SOURCE_ORDINAL, - List.of(), List.of())); + List.of(), List.of(), null)); } QuarkusComponentTestExtension(QuarkusComponentTestConfiguration baseConfiguration) { @@ -252,7 +253,7 @@ public void afterEach(ExtensionContext context) throws Exception { @Override public void postProcessTestInstance(Object testInstance, ExtensionContext context) throws Exception { long start = System.nanoTime(); - context.getRoot().getStore(NAMESPACE).put(KEY_TEST_INSTANCE, testInstance); + store(context).put(KEY_TEST_INSTANCE, testInstance); LOG.debugf("postProcessTestInstance: %s ms", TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start)); } @@ -322,7 +323,7 @@ && isTestMethod(parameterContext.getDeclaringExecutable()) public Object resolveParameter(ParameterContext parameterContext, ExtensionContext context) throws ParameterResolutionException { @SuppressWarnings("unchecked") - List> injectedParams = context.getRoot().getStore(NAMESPACE).get(KEY_INJECTED_PARAMS, List.class); + List> injectedParams = store(context).get(KEY_INJECTED_PARAMS, List.class); ArcContainer container = Arc.container(); BeanManager beanManager = container.beanManager(); java.lang.reflect.Type requiredType = parameterContext.getParameter().getParameterizedType(); @@ -339,7 +340,7 @@ public Object resolveParameter(ParameterContext parameterContext, ExtensionConte private void destroyDependentTestMethodParams(ExtensionContext context) { @SuppressWarnings("unchecked") - List> injectedParams = context.getRoot().getStore(NAMESPACE).get(KEY_INJECTED_PARAMS, List.class); + List> injectedParams = store(context).get(KEY_INJECTED_PARAMS, List.class); for (InstanceHandle handle : injectedParams) { if (handle.getBean() != null && handle.getBean().getScope().equals(Dependent.class)) { try { @@ -355,17 +356,17 @@ private void destroyDependentTestMethodParams(ExtensionContext context) { private void buildContainer(ExtensionContext context) { QuarkusComponentTestConfiguration testClassConfiguration = baseConfiguration .update(context.getRequiredTestClass()); - context.getRoot().getStore(NAMESPACE).put(KEY_TEST_CLASS_CONFIG, testClassConfiguration); + store(context).put(KEY_TEST_CLASS_CONFIG, testClassConfiguration); ClassLoader oldTccl = initArcContainer(context, testClassConfiguration); - context.getRoot().getStore(NAMESPACE).put(KEY_OLD_TCCL, oldTccl); + store(context).put(KEY_OLD_TCCL, oldTccl); } @SuppressWarnings("unchecked") private void cleanup(ExtensionContext context) { - ClassLoader oldTccl = context.getRoot().getStore(NAMESPACE).get(KEY_OLD_TCCL, ClassLoader.class); + ClassLoader oldTccl = store(context).get(KEY_OLD_TCCL, ClassLoader.class); Thread.currentThread().setContextClassLoader(oldTccl); - context.getRoot().getStore(NAMESPACE).remove(KEY_CONFIG_MAPPINGS); - Set generatedResources = context.getRoot().getStore(NAMESPACE).get(KEY_GENERATED_RESOURCES, Set.class); + store(context).remove(KEY_CONFIG_MAPPINGS); + Set generatedResources = store(context).get(KEY_GENERATED_RESOURCES, Set.class); for (Path path : generatedResources) { try { LOG.debugf("Delete generated %s", path); @@ -379,7 +380,7 @@ private void cleanup(ExtensionContext context) { @SuppressWarnings("unchecked") private void stopContainer(ExtensionContext context, Lifecycle testInstanceLifecycle) throws Exception { if (testInstanceLifecycle.equals(context.getTestInstanceLifecycle().orElse(Lifecycle.PER_METHOD))) { - for (FieldInjector fieldInjector : (List) context.getRoot().getStore(NAMESPACE) + for (FieldInjector fieldInjector : (List) store(context) .get(KEY_INJECTED_FIELDS, List.class)) { fieldInjector.unset(context.getRequiredTestInstance()); } @@ -392,10 +393,10 @@ private void stopContainer(ExtensionContext context, Lifecycle testInstanceLifec ConfigBeanCreator.clear(); InterceptorMethodCreator.clear(); - SmallRyeConfig config = context.getRoot().getStore(NAMESPACE).get(KEY_CONFIG, SmallRyeConfig.class); + SmallRyeConfig config = store(context).get(KEY_CONFIG, SmallRyeConfig.class); ConfigProviderResolver.instance().releaseConfig(config); ConfigProviderResolver - .setInstance(context.getRoot().getStore(NAMESPACE).get(KEY_OLD_CONFIG_PROVIDER_RESOLVER, + .setInstance(store(context).get(KEY_OLD_CONFIG_PROVIDER_RESOLVER, ConfigProviderResolver.class)); } } @@ -405,15 +406,15 @@ private void startContainer(ExtensionContext context, Lifecycle testInstanceLife // Init ArC Arc.initialize(); - QuarkusComponentTestConfiguration configuration = context.getRoot().getStore(NAMESPACE) - .get(KEY_TEST_CLASS_CONFIG, QuarkusComponentTestConfiguration.class); + QuarkusComponentTestConfiguration configuration = store(context).get(KEY_TEST_CLASS_CONFIG, + QuarkusComponentTestConfiguration.class); Optional testMethod = context.getTestMethod(); if (testMethod.isPresent()) { configuration = configuration.update(testMethod.get()); } ConfigProviderResolver oldConfigProviderResolver = ConfigProviderResolver.instance(); - context.getRoot().getStore(NAMESPACE).put(KEY_OLD_CONFIG_PROVIDER_RESOLVER, oldConfigProviderResolver); + store(context).put(KEY_OLD_CONFIG_PROVIDER_RESOLVER, oldConfigProviderResolver); SmallRyeConfigProviderResolver smallRyeConfigProviderResolver = new SmallRyeConfigProviderResolver(); ConfigProviderResolver.setInstance(smallRyeConfigProviderResolver); @@ -430,28 +431,33 @@ private void startContainer(ExtensionContext context, Lifecycle testInstanceLife new QuarkusComponentTestConfigSource(configuration.configProperties, configuration.configSourceOrdinal)); @SuppressWarnings("unchecked") - Set configMappings = context.getRoot().getStore(NAMESPACE).get(KEY_CONFIG_MAPPINGS, - Set.class); + Set configMappings = store(context).get(KEY_CONFIG_MAPPINGS, Set.class); if (configMappings != null) { // Register the mappings found during bean discovery for (ConfigClassWithPrefix mapping : configMappings) { configBuilder.withMapping(mapping.getKlass(), mapping.getPrefix()); } } + if (configuration.configBuilderCustomizer != null) { + configuration.configBuilderCustomizer.accept(configBuilder); + } SmallRyeConfig config = configBuilder.build(); smallRyeConfigProviderResolver.registerConfig(config, tccl); - context.getRoot().getStore(NAMESPACE).put(KEY_CONFIG, config); + store(context).put(KEY_CONFIG, config); ConfigBeanCreator.setClassLoader(tccl); // Inject fields declated on the test class Object testInstance = context.getRequiredTestInstance(); - context.getRoot().getStore(NAMESPACE).put(KEY_INJECTED_FIELDS, - injectFields(context.getRequiredTestClass(), testInstance)); + store(context).put(KEY_INJECTED_FIELDS, injectFields(context.getRequiredTestClass(), testInstance)); // Injected test method parameters - context.getRoot().getStore(NAMESPACE).put(KEY_INJECTED_PARAMS, new CopyOnWriteArrayList<>()); + store(context).put(KEY_INJECTED_PARAMS, new CopyOnWriteArrayList<>()); } } + private Store store(ExtensionContext context) { + return context.getRoot().getStore(NAMESPACE); + } + private BeanRegistrar registrarForMock(MockBeanConfiguratorImpl mock) { return new BeanRegistrar() { @@ -633,7 +639,7 @@ public void writeResource(Resource resource) throws IOException { }); } - extensionContext.getRoot().getStore(NAMESPACE).put(KEY_GENERATED_RESOURCES, generatedResources); + store(extensionContext).put(KEY_GENERATED_RESOURCES, generatedResources); builder.addAnnotationTransformation(AnnotationsTransformer.appliedToField().whenContainsAny(qualifiers) .whenContainsNone(DotName.createSimple(Inject.class)).thenTransform(t -> t.add(Inject.class))); @@ -783,7 +789,7 @@ public void register(RegistrationContext registrationContext) { .configClassWithPrefix(ConfigMappingBeanCreator.tryLoad(mapping), e.getKey())); } } - extensionContext.getRoot().getStore(NAMESPACE).put(KEY_CONFIG_MAPPINGS, configMappings); + store(extensionContext).put(KEY_CONFIG_MAPPINGS, configMappings); } LOG.debugf("Test injection points analyzed in %s ms [found: %s, mocked: %s]", @@ -850,7 +856,7 @@ public void accept(BytecodeTransformer transformer) { return oldTccl; } - private void processTestInterceptorMethods(Class testClass, ExtensionContext extensionContext, + private void processTestInterceptorMethods(Class testClass, ExtensionContext context, BeanRegistrar.RegistrationContext registrationContext, Set interceptorBindings) { List> annotations = List.of(AroundInvoke.class, PostConstruct.class, PreDestroy.class, AroundConstruct.class); @@ -873,7 +879,7 @@ private void processTestInterceptorMethods(Class testClass, ExtensionContext return ic -> { Object instance = null; if (!Modifier.isStatic(method.getModifiers())) { - Object testInstance = extensionContext.getRoot().getStore(NAMESPACE).get(KEY_TEST_INSTANCE); + Object testInstance = store(context).get(KEY_TEST_INSTANCE); if (testInstance == null) { throw new IllegalStateException("Test instance not available"); } diff --git a/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestExtensionBuilder.java b/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestExtensionBuilder.java index cdf13d9c25c3a..b1fdccb0d1aeb 100644 --- a/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestExtensionBuilder.java +++ b/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestExtensionBuilder.java @@ -5,11 +5,13 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.function.Consumer; import java.util.function.Function; import org.eclipse.microprofile.config.spi.Converter; import io.quarkus.arc.processor.AnnotationsTransformer; +import io.smallrye.config.SmallRyeConfigBuilder; /** * Convenient builder for {@link QuarkusComponentTestExtension}. @@ -32,6 +34,7 @@ public class QuarkusComponentTestExtensionBuilder { private boolean useDefaultConfigProperties = false; private boolean addNestedClassesAsComponents = true; private int configSourceOrdinal = QuarkusComponentTestExtensionBuilder.DEFAULT_CONFIG_SOURCE_ORDINAL; + private Consumer configBuilderCustomizer; /** * The initial set of components under test is derived from the test class. The types of all fields annotated with @@ -111,14 +114,28 @@ public QuarkusComponentTestExtensionBuilder addAnnotationsTransformer(Annotation /** * Add an additional {@link Converter}. By default, the Quarkus-specific converters are registered. * - * @param transformer + * @param converter * @return self + * @see #setConfigBuilderCustomizer(Consumer) */ public QuarkusComponentTestExtensionBuilder addConverter(Converter converter) { configConverters.add(converter); return this; } + /** + * Set the {@link SmallRyeConfigBuilder} customizer. + *

+ * The customizer can affect the configuration of a test method and should be used with caution. + * + * @param customizer + * @return self + */ + public QuarkusComponentTestExtensionBuilder setConfigBuilderCustomizer(Consumer customizer) { + this.configBuilderCustomizer = customizer; + return this; + } + /** * Configure a new mock of a bean. *

@@ -149,7 +166,7 @@ public QuarkusComponentTestExtension build() { return new QuarkusComponentTestExtension(new QuarkusComponentTestConfiguration(Map.copyOf(configProperties), List.copyOf(componentClasses), List.copyOf(mockConfigurators), useDefaultConfigProperties, addNestedClassesAsComponents, configSourceOrdinal, - List.copyOf(annotationsTransformers), converters)); + List.copyOf(annotationsTransformers), converters, configBuilderCustomizer)); } void registerMockBean(MockBeanConfiguratorImpl mock) { diff --git a/test-framework/junit5-component/src/test/java/io/quarkus/test/component/config/ConfigBuilderCustomizerTest.java b/test-framework/junit5-component/src/test/java/io/quarkus/test/component/config/ConfigBuilderCustomizerTest.java new file mode 100644 index 0000000000000..6150ea7aff01a --- /dev/null +++ b/test-framework/junit5-component/src/test/java/io/quarkus/test/component/config/ConfigBuilderCustomizerTest.java @@ -0,0 +1,52 @@ +package io.quarkus.test.component.config; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.function.Consumer; + +import jakarta.inject.Singleton; + +import org.eclipse.microprofile.config.inject.ConfigProperty; +import org.eclipse.microprofile.config.spi.Converter; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.component.QuarkusComponentTestExtension; +import io.quarkus.test.component.TestConfigProperty; +import io.smallrye.config.SmallRyeConfigBuilder; + +public class ConfigBuilderCustomizerTest { + + @RegisterExtension + static final QuarkusComponentTestExtension extension = QuarkusComponentTestExtension.builder() + .setConfigBuilderCustomizer(new Consumer() { + @Override + public void accept(SmallRyeConfigBuilder builder) { + builder.withConverter(Boolean.class, 300, new CustomBooleanConverter()); + } + }).build(); + + @TestConfigProperty(key = "my.boolean", value = "jo") + @Test + public void testBuilderCustomizer(Foo foo) { + assertTrue(foo.boolVal); + } + + @Singleton + public static class Foo { + + @ConfigProperty(name = "my.boolean") + boolean boolVal; + + } + + @SuppressWarnings("serial") + public static class CustomBooleanConverter implements Converter { + + @Override + public Boolean convert(String value) throws IllegalArgumentException, NullPointerException { + return "jo".equals(value) ? true : false; + } + + } +} diff --git a/test-framework/junit5-component/src/test/java/io/quarkus/test/component/config/ConfigConverterExtensionTest.java b/test-framework/junit5-component/src/test/java/io/quarkus/test/component/config/ConfigConverterExtensionTest.java index 2714e2ab0a135..3b4eb785f45f5 100644 --- a/test-framework/junit5-component/src/test/java/io/quarkus/test/component/config/ConfigConverterExtensionTest.java +++ b/test-framework/junit5-component/src/test/java/io/quarkus/test/component/config/ConfigConverterExtensionTest.java @@ -26,7 +26,7 @@ public class ConfigConverterExtensionTest { @TestConfigProperty(key = "my.boolean", value = "jo") @TestConfigProperty(key = "my.duration", value = "5s") @Test - public void testQuarkusDurationConverter(Foo foo) { + public void testConverters(Foo foo) { assertEquals(TimeUnit.SECONDS.toMillis(5), foo.durationVal.toMillis()); assertTrue(foo.boolVal); } diff --git a/test-framework/junit5-component/src/test/java/io/quarkus/test/component/config/ConfigConverterTest.java b/test-framework/junit5-component/src/test/java/io/quarkus/test/component/config/ConfigConverterTest.java index 28c498b785beb..423df0e2e41ea 100644 --- a/test-framework/junit5-component/src/test/java/io/quarkus/test/component/config/ConfigConverterTest.java +++ b/test-framework/junit5-component/src/test/java/io/quarkus/test/component/config/ConfigConverterTest.java @@ -23,7 +23,7 @@ public class ConfigConverterTest { @TestConfigProperty(key = "my.boolean", value = "jo") @TestConfigProperty(key = "my.duration", value = "5s") @Test - public void testQuarkusDurationConverter(Foo foo) { + public void testConverters(Foo foo) { assertEquals(TimeUnit.SECONDS.toMillis(5), foo.durationVal.toMillis()); assertTrue(foo.boolVal); }