diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 02ef6f9631b987..1321c4fb6b526f 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -3286,7 +3286,7 @@ io.quarkus - quarkus-junit5-properties + quarkus-junit5-config ${project.version} diff --git a/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestConfig.java b/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestConfig.java index da1a31e3f43d90..64c58d17c0aade 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestConfig.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/dev/testing/TestConfig.java @@ -8,7 +8,9 @@ import io.quarkus.runtime.annotations.ConfigDocMapKey; import io.quarkus.runtime.annotations.ConfigPhase; import io.quarkus.runtime.annotations.ConfigRoot; +import io.quarkus.runtime.configuration.TrimmedStringConverter; import io.smallrye.config.ConfigMapping; +import io.smallrye.config.WithConverter; import io.smallrye.config.WithDefault; import io.smallrye.config.WithParentName; @@ -45,6 +47,11 @@ public interface TestConfig { @WithDefault("false") boolean displayTestOutput(); + /** + * TODO + */ + Optional classOrderer(); + /** * Tags that should be included for continuous testing. This supports JUnit Tag Expressions. * @@ -77,7 +84,6 @@ public interface TestConfig { * is matched against the test class name (not the file name). *

* This is ignored if include-pattern has been set. - * */ @WithDefault(".*\\.IT[^.]+|.*IT|.*ITCase") Optional excludePattern(); @@ -241,7 +247,6 @@ public interface TestConfig { * is matched against the module groupId:artifactId. *

* This is ignored if include-module-pattern has been set. - * */ Optional excludeModulePattern(); @@ -265,7 +270,7 @@ interface Profile { * then Quarkus will only execute tests that are annotated with a {@code @TestProfile} that has at least one of the * supplied (via the aforementioned system property) tags. */ - Optional> tags(); + Optional> tags(); } interface Container { diff --git a/core/runtime/src/main/java/io/quarkus/runtime/configuration/QuarkusConfigFactory.java b/core/runtime/src/main/java/io/quarkus/runtime/configuration/QuarkusConfigFactory.java index 2adc409fab8162..acdf1b2cf490da 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/configuration/QuarkusConfigFactory.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/configuration/QuarkusConfigFactory.java @@ -1,5 +1,7 @@ package io.quarkus.runtime.configuration; +import org.eclipse.microprofile.config.spi.ConfigProviderResolver; + import io.quarkus.runtime.LaunchMode; import io.smallrye.config.SmallRyeConfig; import io.smallrye.config.SmallRyeConfigFactory; @@ -30,15 +32,12 @@ public SmallRyeConfig getConfigFor(final SmallRyeConfigProviderResolver configPr } public static void setConfig(SmallRyeConfig config) { - SmallRyeConfigProviderResolver configProviderResolver = (SmallRyeConfigProviderResolver) SmallRyeConfigProviderResolver - .instance(); + ConfigProviderResolver configProviderResolver = ConfigProviderResolver.instance(); // Uninstall previous config if (QuarkusConfigFactory.config != null) { configProviderResolver.releaseConfig(QuarkusConfigFactory.config); QuarkusConfigFactory.config = null; } - // Also release the TCCL config, in case that config was not QuarkusConfigFactory.config - configProviderResolver.releaseConfig(Thread.currentThread().getContextClassLoader()); // Install new config if (config != null) { QuarkusConfigFactory.config = config; diff --git a/core/runtime/src/main/java/io/quarkus/runtime/logging/LoggingSetupRecorder.java b/core/runtime/src/main/java/io/quarkus/runtime/logging/LoggingSetupRecorder.java index 2d390ab877db0e..16238c5f45eac8 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/logging/LoggingSetupRecorder.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/logging/LoggingSetupRecorder.java @@ -12,12 +12,10 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Optional; -import java.util.Set; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Function; @@ -31,7 +29,6 @@ import java.util.logging.LogRecord; import org.eclipse.microprofile.config.ConfigProvider; -import org.eclipse.microprofile.config.spi.ConfigSource; import org.jboss.logmanager.ExtFormatter; import org.jboss.logmanager.LogContext; import org.jboss.logmanager.LogContextInitializer; @@ -57,7 +54,6 @@ import io.quarkus.runtime.LaunchMode; import io.quarkus.runtime.RuntimeValue; import io.quarkus.runtime.annotations.Recorder; -import io.quarkus.runtime.configuration.QuarkusConfigBuilderCustomizer; import io.quarkus.runtime.console.ConsoleRuntimeConfig; import io.quarkus.runtime.logging.LogBuildTimeConfig.CategoryBuildTimeConfig; import io.quarkus.runtime.logging.LogRuntimeConfig.CategoryConfig; @@ -67,7 +63,6 @@ import io.quarkus.runtime.logging.LogRuntimeConfig.SocketConfig; import io.quarkus.runtime.shutdown.ShutdownListener; import io.smallrye.config.SmallRyeConfig; -import io.smallrye.config.SmallRyeConfigBuilder; @Recorder public class LoggingSetupRecorder { @@ -87,34 +82,9 @@ public static void handleFailedStart() { public static void handleFailedStart(RuntimeValue>> banner) { SmallRyeConfig config = ConfigProvider.getConfig().unwrap(SmallRyeConfig.class); - // There may be cases where a Config with the mappings is already available, but we can't be sure, so we wrap - // the original Config and map the logging classes. - SmallRyeConfig loggingConfig = new SmallRyeConfigBuilder() - .withCustomizers(new QuarkusConfigBuilderCustomizer()) - .withMapping(LogBuildTimeConfig.class) - .withMapping(LogRuntimeConfig.class) - .withMapping(ConsoleRuntimeConfig.class) - .withSources(new ConfigSource() { - @Override - public Set getPropertyNames() { - Set properties = new HashSet<>(); - config.getPropertyNames().forEach(properties::add); - return properties; - } - - @Override - public String getValue(final String propertyName) { - return config.getRawValue(propertyName); - } - - @Override - public String getName() { - return "Logging Config"; - } - }).build(); - LogRuntimeConfig logRuntimeConfig = loggingConfig.getConfigMapping(LogRuntimeConfig.class); - LogBuildTimeConfig logBuildTimeConfig = loggingConfig.getConfigMapping(LogBuildTimeConfig.class); - ConsoleRuntimeConfig consoleRuntimeConfig = loggingConfig.getConfigMapping(ConsoleRuntimeConfig.class); + LogRuntimeConfig logRuntimeConfig = config.getConfigMapping(LogRuntimeConfig.class); + LogBuildTimeConfig logBuildTimeConfig = config.getConfigMapping(LogBuildTimeConfig.class); + ConsoleRuntimeConfig consoleRuntimeConfig = config.getConfigMapping(ConsoleRuntimeConfig.class); new LoggingSetupRecorder(new RuntimeValue<>(consoleRuntimeConfig)).initializeLogging(logRuntimeConfig, logBuildTimeConfig, DiscoveredLogComponents.ofEmpty(), emptyMap(), false, null, emptyList(), emptyList(), emptyList(), emptyList(), diff --git a/docs/src/main/asciidoc/getting-started-testing.adoc b/docs/src/main/asciidoc/getting-started-testing.adoc index ab8ff2b88e4dce..786766184a4228 100644 --- a/docs/src/main/asciidoc/getting-started-testing.adoc +++ b/docs/src/main/asciidoc/getting-started-testing.adoc @@ -469,6 +469,8 @@ To get around this Quarkus supports the idea of a test profile. If a test has a run test then Quarkus will be shut down and started with the new profile before running the tests. This is obviously a bit slower, as it adds a shutdown/startup cycle to the test time, but gives a great deal of flexibility. +// TODO - Rewrite docs + To reduce the amount of times Quarkus needs to restart, `io.quarkus.test.junit.util.QuarkusTestProfileAwareClassOrderer` is registered as a global `ClassOrderer` as described in the link:https://junit.org/junit5/docs/current/user-guide/#writing-tests-test-execution-order-classes[JUnit 5 User Guide]. diff --git a/test-framework/common/src/main/java/io/quarkus/test/common/LauncherUtil.java b/test-framework/common/src/main/java/io/quarkus/test/common/LauncherUtil.java index 22a4ed3fcca754..63c687909ef0bc 100644 --- a/test-framework/common/src/main/java/io/quarkus/test/common/LauncherUtil.java +++ b/test-framework/common/src/main/java/io/quarkus/test/common/LauncherUtil.java @@ -22,13 +22,10 @@ import java.util.regex.Pattern; import org.eclipse.microprofile.config.Config; +import org.eclipse.microprofile.config.ConfigProvider; -import io.quarkus.runtime.LaunchMode; -import io.quarkus.runtime.configuration.ConfigUtils; -import io.quarkus.runtime.configuration.QuarkusConfigFactory; import io.quarkus.test.common.http.TestHTTPResourceManager; import io.quarkus.utilities.OS; -import io.smallrye.config.SmallRyeConfig; public final class LauncherUtil { @@ -38,9 +35,7 @@ private LauncherUtil() { } public static Config installAndGetSomeConfig() { - SmallRyeConfig config = ConfigUtils.configBuilder(false, LaunchMode.NORMAL).build(); - QuarkusConfigFactory.setConfig(config); - return config; + return ConfigProvider.getConfig(); } /** 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 d5f606290bebe4..176751c654bf46 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 @@ -38,8 +38,6 @@ import org.jboss.jandex.DotName; import org.jboss.jandex.IndexView; -import io.smallrye.config.SmallRyeConfigProviderResolver; - /** * Manages {@link QuarkusTestResourceLifecycleManager} */ @@ -214,18 +212,6 @@ public void close() { throw new RuntimeException("Unable to stop Quarkus test resource " + entry.getTestResource(), e); } } - // TODO using QuarkusConfigFactory.setConfig(null) here makes continuous testing fail, - // e.g. in io.quarkus.hibernate.orm.HibernateHotReloadTestCase - // or io.quarkus.opentelemetry.deployment.OpenTelemetryContinuousTestingTest; - // maybe this cleanup is not really necessary and just "doesn't hurt" because - // the released config is still cached in QuarkusConfigFactory#config - // and will be restored soon after when QuarkusConfigFactory#getConfigFor is called? - // In that case we should remove this cleanup. - try { - ((SmallRyeConfigProviderResolver) SmallRyeConfigProviderResolver.instance()) - .releaseConfig(Thread.currentThread().getContextClassLoader()); - } catch (Throwable ignored) { - } configProperties.clear(); } diff --git a/test-framework/junit5-config/pom.xml b/test-framework/junit5-config/pom.xml new file mode 100644 index 00000000000000..9e5a21c6e54bb2 --- /dev/null +++ b/test-framework/junit5-config/pom.xml @@ -0,0 +1,35 @@ + + + 4.0.0 + + + io.quarkus + quarkus-test-framework + 999-SNAPSHOT + + + quarkus-junit5-config + Quarkus - Test Framework - JUnit 5 Config + + + + org.junit.jupiter + junit-jupiter-api + + + io.smallrye.config + smallrye-config + + + io.quarkus + quarkus-core + + + io.quarkus + quarkus-core-deployment + + + + diff --git a/test-framework/junit5-config/src/main/java/io/quarkus/test/config/LoggingSetupExtension.java b/test-framework/junit5-config/src/main/java/io/quarkus/test/config/LoggingSetupExtension.java new file mode 100644 index 00000000000000..13ae524d8de99c --- /dev/null +++ b/test-framework/junit5-config/src/main/java/io/quarkus/test/config/LoggingSetupExtension.java @@ -0,0 +1,14 @@ +package io.quarkus.test.config; + +import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.Extension; +import org.junit.jupiter.api.extension.ExtensionContext; + +import io.quarkus.runtime.logging.LoggingSetupRecorder; + +public class LoggingSetupExtension implements Extension, BeforeAllCallback { + @Override + public void beforeAll(final ExtensionContext context) throws Exception { + LoggingSetupRecorder.handleFailedStart(); + } +} diff --git a/test-framework/junit5-config/src/main/java/io/quarkus/test/config/QuarkusClassOrderer.java b/test-framework/junit5-config/src/main/java/io/quarkus/test/config/QuarkusClassOrderer.java new file mode 100644 index 00000000000000..1a39f11b12884e --- /dev/null +++ b/test-framework/junit5-config/src/main/java/io/quarkus/test/config/QuarkusClassOrderer.java @@ -0,0 +1,45 @@ +package io.quarkus.test.config; + +import org.eclipse.microprofile.config.ConfigProvider; +import org.eclipse.microprofile.config.spi.ConfigProviderResolver; +import org.junit.jupiter.api.ClassOrderer; +import org.junit.jupiter.api.ClassOrdererContext; +import org.junit.platform.commons.util.ReflectionUtils; + +import io.quarkus.deployment.dev.testing.TestConfig; +import io.quarkus.runtime.LaunchMode; +import io.smallrye.config.SmallRyeConfig; + +public class QuarkusClassOrderer implements ClassOrderer { + private final ClassOrderer delegate; + + static { + TestConfigProviderResolver resolver = new TestConfigProviderResolver(); + ConfigProviderResolver.setInstance(resolver); + resolver.getConfig(LaunchMode.TEST); + } + + public QuarkusClassOrderer() { + SmallRyeConfig config = ConfigProvider.getConfig().unwrap(SmallRyeConfig.class); + TestConfig testConfig = config.getConfigMapping(TestConfig.class); + + delegate = testConfig.classOrderer() + .map(klass -> ReflectionUtils.tryToLoadClass(klass) + .andThenTry(ReflectionUtils::newInstance) + .andThenTry(instance -> (ClassOrderer) instance) + .toOptional().orElse(EMPTY)) + .orElse(EMPTY); + } + + @Override + public void orderClasses(final ClassOrdererContext context) { + delegate.orderClasses(context); + } + + private static final ClassOrderer EMPTY = new ClassOrderer() { + @Override + public void orderClasses(final ClassOrdererContext context) { + + } + }; +} diff --git a/test-framework/junit5-config/src/main/java/io/quarkus/test/config/TestConfigProviderResolver.java b/test-framework/junit5-config/src/main/java/io/quarkus/test/config/TestConfigProviderResolver.java new file mode 100644 index 00000000000000..458f28c26bf544 --- /dev/null +++ b/test-framework/junit5-config/src/main/java/io/quarkus/test/config/TestConfigProviderResolver.java @@ -0,0 +1,74 @@ +package io.quarkus.test.config; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; + +import org.eclipse.microprofile.config.Config; + +import io.quarkus.deployment.dev.testing.TestConfig; +import io.quarkus.runtime.LaunchMode; +import io.quarkus.runtime.configuration.ConfigUtils; +import io.smallrye.config.SmallRyeConfig; +import io.smallrye.config.SmallRyeConfigBuilder; +import io.smallrye.config.SmallRyeConfigProviderResolver; + +public class TestConfigProviderResolver extends SmallRyeConfigProviderResolver { + private static final SmallRyeConfigProviderResolver resolver = (SmallRyeConfigProviderResolver) SmallRyeConfigProviderResolver + .instance(); + + private static final Map configs = new ConcurrentHashMap<>(); + private static final ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + + @Override + public Config getConfig() { + return resolver.getConfig(); + } + + public Config getConfig(final LaunchMode mode) { + if (classLoader.equals(Thread.currentThread().getContextClassLoader())) { + resolver.releaseConfig(classLoader); + SmallRyeConfig config = configs.computeIfAbsent(mode, new Function() { + @Override + public SmallRyeConfig apply(final LaunchMode launchMode) { + return ConfigUtils.configBuilder(false, true, mode) + .withProfile(mode.getDefaultProfile()) + .withMapping(TestConfig.class, "quarkus.test") + .build(); + } + }); + resolver.registerConfig(config, classLoader); + return config; + } + throw new IllegalStateException(); + } + + public void restoreConfig() { + if (classLoader.equals(Thread.currentThread().getContextClassLoader())) { + resolver.releaseConfig(classLoader); + resolver.registerConfig(configs.get(LaunchMode.TEST), classLoader); + } else { + throw new IllegalStateException(); + } + } + + @Override + public Config getConfig(final ClassLoader loader) { + return resolver.getConfig(loader); + } + + @Override + public SmallRyeConfigBuilder getBuilder() { + return resolver.getBuilder(); + } + + @Override + public void registerConfig(final Config config, final ClassLoader classLoader) { + resolver.registerConfig(config, classLoader); + } + + @Override + public void releaseConfig(final Config config) { + resolver.releaseConfig(config); + } +} diff --git a/test-framework/junit5-config/src/main/resources/META-INF/services/org.junit.jupiter.api.extension.Extension b/test-framework/junit5-config/src/main/resources/META-INF/services/org.junit.jupiter.api.extension.Extension new file mode 100644 index 00000000000000..a91b69862a876f --- /dev/null +++ b/test-framework/junit5-config/src/main/resources/META-INF/services/org.junit.jupiter.api.extension.Extension @@ -0,0 +1 @@ +io.quarkus.test.config.LoggingSetupExtension diff --git a/test-framework/junit5-config/src/main/resources/junit-platform.properties b/test-framework/junit5-config/src/main/resources/junit-platform.properties new file mode 100644 index 00000000000000..d24c3ed5820f5e --- /dev/null +++ b/test-framework/junit5-config/src/main/resources/junit-platform.properties @@ -0,0 +1,2 @@ +junit.jupiter.extensions.autodetection.enabled=true +junit.jupiter.testclass.order.default=io.quarkus.test.config.QuarkusClassOrderer diff --git a/test-framework/junit5-internal/pom.xml b/test-framework/junit5-internal/pom.xml index 526869cab070b5..9bd5cf5b47f477 100644 --- a/test-framework/junit5-internal/pom.xml +++ b/test-framework/junit5-internal/pom.xml @@ -39,6 +39,10 @@ junit-jupiter-engine compile + + io.quarkus + quarkus-junit5-config + io.quarkus quarkus-core diff --git a/test-framework/junit5-internal/src/main/java/io/quarkus/test/QuarkusDevModeTest.java b/test-framework/junit5-internal/src/main/java/io/quarkus/test/QuarkusDevModeTest.java index 1cad2435abab80..c3fc8e5376ec07 100644 --- a/test-framework/junit5-internal/src/main/java/io/quarkus/test/QuarkusDevModeTest.java +++ b/test-framework/junit5-internal/src/main/java/io/quarkus/test/QuarkusDevModeTest.java @@ -28,6 +28,7 @@ import java.util.logging.LogRecord; import java.util.stream.Stream; +import org.eclipse.microprofile.config.spi.ConfigProviderResolver; import org.jboss.logmanager.Logger; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.exporter.ExplodedExporter; @@ -56,6 +57,7 @@ import io.quarkus.test.common.TestConfigUtil; import io.quarkus.test.common.TestResourceManager; import io.quarkus.test.common.http.TestHTTPResourceManager; +import io.quarkus.test.config.TestConfigProviderResolver; /** * A test extension for black-box testing of Quarkus development mode in extensions. @@ -227,11 +229,10 @@ public Object createTestInstance(TestInstanceFactoryContext factoryContext, Exte } @Override - public void beforeAll(ExtensionContext context) throws Exception { + public void beforeAll(ExtensionContext context) { + ((TestConfigProviderResolver) ConfigProviderResolver.instance()).getConfig(LaunchMode.DEVELOPMENT); TestConfigUtil.cleanUp(); GroovyClassValue.disable(); - //set the right launch mode in the outer CL, used by the HTTP host config source - LaunchMode.set(LaunchMode.DEVELOPMENT); originalRootLoggerHandlers = rootLogger.getHandlers(); rootLogger.addHandler(inMemoryLogHandler); } @@ -313,7 +314,7 @@ public void close() throws Throwable { } @Override - public void afterAll(ExtensionContext context) throws Exception { + public void afterAll(ExtensionContext context) { for (Map.Entry e : oldSystemProps.entrySet()) { if (e.getValue() == null) { System.clearProperty(e.getKey()); @@ -326,6 +327,7 @@ public void afterAll(ExtensionContext context) throws Exception { inMemoryLogHandler.setFilter(null); ClearCache.clearCaches(); TestConfigUtil.cleanUp(); + ((TestConfigProviderResolver) ConfigProviderResolver.instance()).restoreConfig(); } @Override diff --git a/test-framework/junit5-properties/pom.xml b/test-framework/junit5-properties/pom.xml deleted file mode 100644 index 63fb5bf6fe370c..00000000000000 --- a/test-framework/junit5-properties/pom.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - 4.0.0 - - - io.quarkus - quarkus-test-framework - 999-SNAPSHOT - - - quarkus-junit5-properties - Quarkus - Test Framework - JUnit 5 - Properties - - Contains junit-platform.properties in a "user-excludable" way - until /~https://github.com/junit-team/junit5/issues/2794 is available. - - - - - diff --git a/test-framework/junit5-properties/src/main/resources/junit-platform.properties b/test-framework/junit5-properties/src/main/resources/junit-platform.properties deleted file mode 100644 index cdac134076ffb3..00000000000000 --- a/test-framework/junit5-properties/src/main/resources/junit-platform.properties +++ /dev/null @@ -1,2 +0,0 @@ -junit.jupiter.extensions.autodetection.enabled=true -junit.jupiter.testclass.order.default=io.quarkus.test.junit.util.QuarkusTestProfileAwareClassOrderer diff --git a/test-framework/junit5/pom.xml b/test-framework/junit5/pom.xml index 449f8fda37df57..c4cb92b72454d4 100644 --- a/test-framework/junit5/pom.xml +++ b/test-framework/junit5/pom.xml @@ -37,7 +37,7 @@ io.quarkus - quarkus-junit5-properties + quarkus-junit5-config org.junit.jupiter diff --git a/test-framework/junit5/src/main/java/io/quarkus/test/junit/AbstractJvmQuarkusTestExtension.java b/test-framework/junit5/src/main/java/io/quarkus/test/junit/AbstractJvmQuarkusTestExtension.java index a36ee0cc8ac64c..cc9b51b225eff5 100644 --- a/test-framework/junit5/src/main/java/io/quarkus/test/junit/AbstractJvmQuarkusTestExtension.java +++ b/test-framework/junit5/src/main/java/io/quarkus/test/junit/AbstractJvmQuarkusTestExtension.java @@ -12,6 +12,7 @@ import java.util.Collection; import java.util.Deque; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; @@ -20,6 +21,7 @@ import jakarta.enterprise.inject.Alternative; +import org.eclipse.microprofile.config.ConfigProvider; import org.jboss.jandex.Index; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.extension.ConditionEvaluationResult; @@ -38,11 +40,13 @@ import io.quarkus.bootstrap.workspace.SourceDir; import io.quarkus.bootstrap.workspace.WorkspaceModule; import io.quarkus.deployment.dev.testing.CurrentTestApplication; +import io.quarkus.deployment.dev.testing.TestConfig; import io.quarkus.paths.PathList; import io.quarkus.runtime.LaunchMode; import io.quarkus.test.common.PathTestHelper; import io.quarkus.test.common.RestorableSystemProperties; import io.quarkus.test.common.TestClassIndexer; +import io.smallrye.config.SmallRyeConfig; public class AbstractJvmQuarkusTestExtension extends AbstractQuarkusTestWithContextExtension implements ExecutionCondition { @@ -279,8 +283,10 @@ public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext con if (context.getTestInstance().isPresent()) { return ConditionEvaluationResult.enabled("Quarkus Test Profile tags only affect classes"); } - String tagsStr = System.getProperty("quarkus.test.profile.tags"); - if ((tagsStr == null) || tagsStr.isEmpty()) { + SmallRyeConfig config = ConfigProvider.getConfig().unwrap(SmallRyeConfig.class); + TestConfig testConfig = config.getConfigMapping(TestConfig.class); + Optional> tags = testConfig.profile().tags(); + if (tags.isEmpty() || tags.get().isEmpty()) { return ConditionEvaluationResult.enabled("No Quarkus Test Profile tags"); } Class testProfile = getQuarkusTestProfile(context); @@ -295,8 +301,7 @@ public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext con throw new RuntimeException(e); } Set testProfileTags = profileInstance.tags(); - String[] tags = tagsStr.split(","); - for (String tag : tags) { + for (String tag : tags.get()) { String trimmedTag = tag.trim(); if (testProfileTags.contains(trimmedTag)) { return ConditionEvaluationResult.enabled("Tag '" + trimmedTag + "' is present on '" + testProfile diff --git a/test-framework/junit5/src/main/java/io/quarkus/test/junit/BasicLoggingEnabler.java b/test-framework/junit5/src/main/java/io/quarkus/test/junit/BasicLoggingEnabler.java deleted file mode 100644 index 1edc18fe9c662c..00000000000000 --- a/test-framework/junit5/src/main/java/io/quarkus/test/junit/BasicLoggingEnabler.java +++ /dev/null @@ -1,146 +0,0 @@ -package io.quarkus.test.junit; - -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; - -import org.eclipse.microprofile.config.Config; -import org.eclipse.microprofile.config.spi.ConfigProviderResolver; -import org.junit.jupiter.api.extension.BeforeAllCallback; -import org.junit.jupiter.api.extension.ExtensionContext; - -import io.quarkus.bootstrap.logging.InitialConfigurator; -import io.quarkus.runtime.LaunchMode; -import io.quarkus.runtime.configuration.ConfigUtils; - -/** - * A (global) JUnit callback that enables/sets up basic logging if logging has not already been set up. - *

- * This is useful for getting log output from non-Quarkus tests (if executed separately or before the first Quarkus test), - * but also for getting instant log output from {@code QuarkusTestResourceLifecycleManagers} etc. - *

- * This callback can be disabled via {@link #CFGKEY_ENABLED} in {@code junit-platform.properties} or via system property. - */ -public class BasicLoggingEnabler implements BeforeAllCallback { - - private static final String CFGKEY_ENABLED = "junit.quarkus.enable-basic-logging"; - private static Boolean enabled; - - private static final CompletableFuture configFuture; - - // internal flag, not meant to be used like CFGKEY_ENABLED - private static final boolean VERBOSE = Boolean.getBoolean(BasicLoggingEnabler.class.getName() + ".verbose"); - private static final long staticInitStart; - - // to speed things up a little, eager async loading of the config that will be looked up in LoggingSetupRecorder - // downside: doesn't obey CFGKEY_ENABLED, but that should be bearable - static { - staticInitStart = VERBOSE ? System.currentTimeMillis() : 0; - // e.g. continuous testing has everything set up already (DELAYED_HANDLER is active) - if (!InitialConfigurator.DELAYED_HANDLER.isActivated() - // at least respect CFGKEY_ENABLED if set as system property - && Boolean.parseBoolean(System.getProperty(CFGKEY_ENABLED, "true"))) { - - configFuture = CompletableFuture.supplyAsync(BasicLoggingEnabler::buildConfig); - } else { - configFuture = CompletableFuture.completedFuture(null); - } - } - - @Override - public synchronized void beforeAll(ExtensionContext context) { - if (enabled == null) { - enabled = context.getConfigurationParameter(CFGKEY_ENABLED).map(Boolean::valueOf).orElse(Boolean.TRUE); - } - if (!enabled || InitialConfigurator.DELAYED_HANDLER.isActivated()) { - return; - } - - var beforeAllStart = VERBOSE ? System.currentTimeMillis() : 0; - if (VERBOSE) { - System.out.printf("BasicLoggingEnabler took %s ms from static init to start of beforeAll()%n", - beforeAllStart - staticInitStart); - } - - ////////////////////// - // get the test config - - Config testConfig; - try { - testConfig = configFuture.get(); - // highly unlikely, but things might have changed since the static block decided to _not_ load the config - if (testConfig == null) { - testConfig = buildConfig(); - } - } catch (Exception e) { - // don't be too noisy (don't log the stacktrace) - System.err.printf("BasicLoggingEnabler failed to retrieve config: %s%n", - e instanceof ExecutionException ? ((ExecutionException) e).getCause() : e); - if (VERBOSE) { - e.printStackTrace(); - } - return; - } - - /////////////////////////// - // register the test config - - var configProviderResolver = ConfigProviderResolver.instance(); - var tccl = Thread.currentThread().getContextClassLoader(); - Config configToRestore; - try { - configProviderResolver.registerConfig(testConfig, tccl); - configToRestore = null; - } catch (IllegalStateException e) { - if (VERBOSE) { - System.out.println("BasicLoggingEnabler is swapping config after " + e); - } - // a config is already registered, which can happen in rare cases, - // so remember it for later restore, release it and register the test config instead - configToRestore = configProviderResolver.getConfig(); - configProviderResolver.releaseConfig(configToRestore); - configProviderResolver.registerConfig(testConfig, tccl); - } - - /////////////////// - // activate logging - - try { - IntegrationTestUtil.activateLogging(); - } catch (RuntimeException e) { - // don't be too noisy (don't log the stacktrace by default) - System.err.println("BasicLoggingEnabler failed to enable basic logging: " + e); - if (VERBOSE) { - e.printStackTrace(); - } - } finally { - // release the config that was registered previously so that tests that try to register their own config - // don't fail with: - // "IllegalStateException: SRCFG00017: Configuration already registered for the given class loader" - // also, a possible recreation of basically the same config for a later test class will consume far less time - configProviderResolver.releaseConfig(testConfig); - // if another config was already registered, restore/re-register it now - if (configToRestore != null) { - configProviderResolver.registerConfig(configToRestore, tccl); - } - } - if (VERBOSE) { - System.out.printf("BasicLoggingEnabler took %s ms from start of beforeAll() to end%n", - System.currentTimeMillis() - beforeAllStart); - } - } - - private static Config buildConfig() { - // make sure to load ConfigSources with the proper LaunchMode in place - LaunchMode.set(LaunchMode.TEST); - // notes: - // - addDiscovered might seem a bit much, but this ensures that yaml files are loaded (if extension is around) - // - LaunchMode.NORMAL instead of TEST avoids failing on missing RuntimeOverrideConfigSource$$GeneratedMapHolder - var start = VERBOSE ? System.currentTimeMillis() : 0; - var testConfig = ConfigUtils.configBuilder(true, true, LaunchMode.NORMAL).build(); - if (VERBOSE) { - System.out.printf("BasicLoggingEnabler took %s ms to load config%n", System.currentTimeMillis() - start); - testConfig.getConfigSources().forEach(s -> System.out.println("BasicLoggingEnabler ConfigSource: " + s)); - } - return testConfig; - } -} diff --git a/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusTestExtension.java b/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusTestExtension.java index 8d54d14d940746..70ee1a6ff9db91 100644 --- a/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusTestExtension.java +++ b/test-framework/junit5/src/main/java/io/quarkus/test/junit/QuarkusTestExtension.java @@ -262,7 +262,9 @@ public Thread newThread(Runnable r) { .orElse(Duration.of(10, ChronoUnit.MINUTES)); hangTaskKey = hangDetectionExecutor.schedule(hangDetectionTask, hangTimeout.toMillis(), TimeUnit.MILLISECONDS); } - ConfigProviderResolver.setInstance(new RunningAppConfigResolver(runningQuarkusApplication)); + ConfigProviderResolver.instance().registerConfig( + new RunningAppConfigResolver(runningQuarkusApplication).getConfig(), + runningQuarkusApplication.getClassLoader()); RestorableSystemProperties restorableSystemProperties = RestorableSystemProperties.setProperties( Collections.singletonMap("test.url", TestHTTPResourceManager.getUri(runningQuarkusApplication))); @@ -370,7 +372,6 @@ public void beforeTestExecution(ExtensionContext context) throws Exception { } } else { throwBootFailureException(); - return; } } @@ -404,7 +405,6 @@ public void beforeEach(ExtensionContext context) throws Exception { } } else { throwBootFailureException(); - return; } } @@ -1154,19 +1154,16 @@ protected void doClose() { } finally { runningQuarkusApplication = null; Thread.currentThread().setContextClassLoader(old); - ConfigProviderResolver.setInstance(null); } } } class FailedCleanup implements ExtensionContext.Store.CloseableResource { - @Override public void close() { shutdownHangDetection(); firstException = null; failedBoot = false; - ConfigProviderResolver.setInstance(null); } } diff --git a/test-framework/junit5/src/main/java/io/quarkus/test/junit/RunningAppConfigResolver.java b/test-framework/junit5/src/main/java/io/quarkus/test/junit/RunningAppConfigResolver.java index e3ec91cd40b6a1..54580344058a48 100644 --- a/test-framework/junit5/src/main/java/io/quarkus/test/junit/RunningAppConfigResolver.java +++ b/test-framework/junit5/src/main/java/io/quarkus/test/junit/RunningAppConfigResolver.java @@ -44,7 +44,7 @@ public Iterable getConfigSources() { @Override public ConfigValue getConfigValue(final String propertyName) { - throw illegalStateException(); + return runningQuarkusApplication.getConfigValue(propertyName, ConfigValue.class).get(); } @Override diff --git a/test-framework/junit5/src/main/resources/META-INF/services/org.junit.jupiter.api.extension.Extension b/test-framework/junit5/src/main/resources/META-INF/services/org.junit.jupiter.api.extension.Extension deleted file mode 100644 index 3d466fc6d2db54..00000000000000 --- a/test-framework/junit5/src/main/resources/META-INF/services/org.junit.jupiter.api.extension.Extension +++ /dev/null @@ -1 +0,0 @@ -io.quarkus.test.junit.BasicLoggingEnabler diff --git a/test-framework/junit5/src/main/resources/application.properties b/test-framework/junit5/src/main/resources/application.properties new file mode 100644 index 00000000000000..9b42adcf2622b0 --- /dev/null +++ b/test-framework/junit5/src/main/resources/application.properties @@ -0,0 +1,2 @@ +config_ordinal=-2147483548 +quarkus.test.class-orderer=io.quarkus.test.junit.util.QuarkusTestProfileAwareClassOrderer diff --git a/test-framework/pom.xml b/test-framework/pom.xml index 888a8dd2229283..fef1a28244cb7b 100644 --- a/test-framework/pom.xml +++ b/test-framework/pom.xml @@ -21,8 +21,8 @@ derby kubernetes-client openshift-client + junit5-config junit5-internal - junit5-properties junit5 junit5-component junit5-mockito