From adedb08a6394ed8f53a9207921e25eb5a60f0e81 Mon Sep 17 00:00:00 2001 From: Roberto Cortez Date: Tue, 19 Nov 2024 17:56:07 +0000 Subject: [PATCH] Register a Config instance to bootstrap tests --- bom/application/pom.xml | 2 +- .../deployment/dev/testing/TestConfig.java | 16 +- .../configuration/QuarkusConfigFactory.java | 14 +- .../asciidoc/getting-started-testing.adoc | 22 +-- .../common/runtime/util/SchedulerUtils.java | 6 +- .../VertxEventBusConsumerRecorder.java | 6 +- .../src/main/resources/application.properties | 4 +- .../src/main/resources/application.properties | 4 +- .../src/main/resources/application.properties | 4 +- .../src/main/resources/application.properties | 4 +- .../io/quarkus/test/common/LauncherUtil.java | 9 +- .../test/common/TestResourceManager.java | 14 -- test-framework/junit5-config/pom.xml | 35 +++++ .../test/config/ConfigLauncherSession.java | 28 ++++ .../test/config/LoggingSetupExtension.java | 17 ++ .../test/config/QuarkusClassOrderer.java | 41 +++++ .../config/TestConfigProviderResolver.java | 99 ++++++++++++ .../org.junit.jupiter.api.extension.Extension | 1 + ....platform.launcher.LauncherSessionListener | 1 + .../main/resources/junit-platform.properties | 2 + test-framework/junit5-internal/pom.xml | 4 + .../io/quarkus/test/QuarkusDevModeTest.java | 10 +- test-framework/junit5-properties/pom.xml | 23 --- .../main/resources/junit-platform.properties | 2 - test-framework/junit5/pom.xml | 2 +- .../AbstractJvmQuarkusTestExtension.java | 13 +- .../test/junit/BasicLoggingEnabler.java | 146 ------------------ .../test/junit/QuarkusTestExtension.java | 12 +- .../test/junit/RunningAppConfigResolver.java | 2 +- .../QuarkusTestProfileAwareClassOrderer.java | 82 +++++----- .../org.junit.jupiter.api.extension.Extension | 1 - ...arkusTestProfileAwareClassOrdererTest.java | 27 +--- test-framework/pom.xml | 2 +- 33 files changed, 337 insertions(+), 318 deletions(-) create mode 100644 test-framework/junit5-config/pom.xml create mode 100644 test-framework/junit5-config/src/main/java/io/quarkus/test/config/ConfigLauncherSession.java create mode 100644 test-framework/junit5-config/src/main/java/io/quarkus/test/config/LoggingSetupExtension.java create mode 100644 test-framework/junit5-config/src/main/java/io/quarkus/test/config/QuarkusClassOrderer.java create mode 100644 test-framework/junit5-config/src/main/java/io/quarkus/test/config/TestConfigProviderResolver.java create mode 100644 test-framework/junit5-config/src/main/resources/META-INF/services/org.junit.jupiter.api.extension.Extension create mode 100644 test-framework/junit5-config/src/main/resources/META-INF/services/org.junit.platform.launcher.LauncherSessionListener create mode 100644 test-framework/junit5-config/src/main/resources/junit-platform.properties delete mode 100644 test-framework/junit5-properties/pom.xml delete mode 100644 test-framework/junit5-properties/src/main/resources/junit-platform.properties delete mode 100644 test-framework/junit5/src/main/java/io/quarkus/test/junit/BasicLoggingEnabler.java delete mode 100644 test-framework/junit5/src/main/resources/META-INF/services/org.junit.jupiter.api.extension.Extension diff --git a/bom/application/pom.xml b/bom/application/pom.xml index 7b40168b0a875..478050c3426c3 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -3284,7 +3284,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 da1a31e3f43d9..f4c3b9eb0f4ae 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,16 @@ public interface TestConfig { @WithDefault("false") boolean displayTestOutput(); + /** + * The FQCN of the JUnit ClassOrderer to use. If the class cannot be found, it fallbacks to JUnit + * default behaviour which does not set a ClassOrderer at all. + * + * @see JUnit Class + * Order + */ + @WithDefault("io.quarkus.test.junit.util.QuarkusTestProfileAwareClassOrderer") + Optional classOrderer(); + /** * Tags that should be included for continuous testing. This supports JUnit Tag Expressions. * @@ -77,7 +89,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 +252,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 +275,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 2adc409fab816..85f778217b946 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; @@ -12,13 +14,6 @@ public final class QuarkusConfigFactory extends SmallRyeConfigFactory { private static volatile SmallRyeConfig config; - /** - * Construct a new instance. Called by service loader. - */ - public QuarkusConfigFactory() { - // todo: replace with {@code provider()} post-Java 11 - } - @Override public SmallRyeConfig getConfigFor(final SmallRyeConfigProviderResolver configProviderResolver, final ClassLoader classLoader) { @@ -30,15 +25,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/docs/src/main/asciidoc/getting-started-testing.adoc b/docs/src/main/asciidoc/getting-started-testing.adoc index 916b16f57fd08..e2c0fac89ccbe 100644 --- a/docs/src/main/asciidoc/getting-started-testing.adoc +++ b/docs/src/main/asciidoc/getting-started-testing.adoc @@ -472,24 +472,10 @@ a bit slower, as it adds a shutdown/startup cycle to the test time, but gives a 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]. -The behavior of this `ClassOrderer` is configurable via `junit-platform.properties` (see the source code or javadoc for more details). -It can also be disabled entirely by setting another `ClassOrderer` that is provided by JUnit 5 or even your own custom one. + -Please note that as of JUnit 5.8.2 link:https://github.com/junit-team/junit5/issues/2794[only a single `junit-platform.properties` is picked up and a warning is logged if more than one is found]. -If you encounter such warnings, you can get rid of them by removing the Quarkus-supplied `junit-platform.properties` from the classpath via an exclusion: -[source,xml] ----- - - io.quarkus - quarkus-junit5 - test - - - io.quarkus - quarkus-junit5-properties - - - ----- +The behavior of this `ClassOrderer` is configurable via `application.properties` using the property +`quarkus.test.class-orderer`. The property accepts the FQCN of the `ClassOrderer` to use. If the class cannot be found, +it fallbacks to JUnit default behaviour which does not set a `ClassOrderer` at all. It can also be disabled entirely by +setting another `ClassOrderer` that is provided by JUnit 5 or even your own custom one. === Writing a Profile diff --git a/extensions/scheduler/common/src/main/java/io/quarkus/scheduler/common/runtime/util/SchedulerUtils.java b/extensions/scheduler/common/src/main/java/io/quarkus/scheduler/common/runtime/util/SchedulerUtils.java index 96b87a5e33ce3..289b9beef6876 100644 --- a/extensions/scheduler/common/src/main/java/io/quarkus/scheduler/common/runtime/util/SchedulerUtils.java +++ b/extensions/scheduler/common/src/main/java/io/quarkus/scheduler/common/runtime/util/SchedulerUtils.java @@ -14,7 +14,7 @@ import jakarta.enterprise.inject.Instance; import org.eclipse.microprofile.config.Config; -import org.eclipse.microprofile.config.spi.ConfigProviderResolver; +import org.eclipse.microprofile.config.ConfigProvider; import io.quarkus.arc.Arc; import io.quarkus.runtime.configuration.DurationConverter; @@ -147,9 +147,7 @@ private static String adjustExpressionSyntax(String val) { * Adapted from {@link io.smallrye.config.ExpressionConfigSourceInterceptor} */ private static String resolvePropertyExpression(String expr) { - // Force the runtime CL in order to make the DEV UI page work - final ClassLoader cl = SchedulerUtils.class.getClassLoader(); - final Config config = ConfigProviderResolver.instance().getConfig(cl); + final Config config = ConfigProvider.getConfig(); final Expression expression = Expression.compile(expr, LENIENT_SYNTAX, NO_TRIM); final String expanded = expression.evaluate(new BiConsumer, StringBuilder>() { @Override diff --git a/extensions/vertx/runtime/src/main/java/io/quarkus/vertx/runtime/VertxEventBusConsumerRecorder.java b/extensions/vertx/runtime/src/main/java/io/quarkus/vertx/runtime/VertxEventBusConsumerRecorder.java index 145e34e844bd8..469458e5117f2 100644 --- a/extensions/vertx/runtime/src/main/java/io/quarkus/vertx/runtime/VertxEventBusConsumerRecorder.java +++ b/extensions/vertx/runtime/src/main/java/io/quarkus/vertx/runtime/VertxEventBusConsumerRecorder.java @@ -19,7 +19,7 @@ import java.util.function.Supplier; import org.eclipse.microprofile.config.Config; -import org.eclipse.microprofile.config.spi.ConfigProviderResolver; +import org.eclipse.microprofile.config.ConfigProvider; import org.jboss.logging.Logger; import io.quarkus.arc.CurrentContextFactory; @@ -310,9 +310,7 @@ private static String lookUpPropertyValue(String propertyValue) { * Adapted from {@link io.smallrye.config.ExpressionConfigSourceInterceptor} */ private static String resolvePropertyExpression(String expr) { - // Force the runtime CL in order to make the DEV UI page work - final ClassLoader cl = VertxEventBusConsumerRecorder.class.getClassLoader(); - final Config config = ConfigProviderResolver.instance().getConfig(cl); + final Config config = ConfigProvider.getConfig(); final Expression expression = Expression.compile(expr, LENIENT_SYNTAX, NO_TRIM); final String expanded = expression.evaluate(new BiConsumer, StringBuilder>() { @Override diff --git a/integration-tests/test-extension/tests/src/test/resources-filtered/projects/project-using-test-callback-from-extension/src/main/resources/application.properties b/integration-tests/test-extension/tests/src/test/resources-filtered/projects/project-using-test-callback-from-extension/src/main/resources/application.properties index 8d698e657885b..442095ca8410c 100644 --- a/integration-tests/test-extension/tests/src/test/resources-filtered/projects/project-using-test-callback-from-extension/src/main/resources/application.properties +++ b/integration-tests/test-extension/tests/src/test/resources-filtered/projects/project-using-test-callback-from-extension/src/main/resources/application.properties @@ -1,3 +1 @@ -quarkus.test.continuous-testing=enabled -# this should not be needed, but something in the tests is setting this to 1234 and confusing the test framework, so set it here to match -quarkus.http.non-application-root-path=1234 \ No newline at end of file +quarkus.test.continuous-testing=enabled \ No newline at end of file diff --git a/integration-tests/test-extension/tests/src/test/resources-filtered/projects/project-using-test-parameter-injection/src/main/resources/application.properties b/integration-tests/test-extension/tests/src/test/resources-filtered/projects/project-using-test-parameter-injection/src/main/resources/application.properties index 8d698e657885b..442095ca8410c 100644 --- a/integration-tests/test-extension/tests/src/test/resources-filtered/projects/project-using-test-parameter-injection/src/main/resources/application.properties +++ b/integration-tests/test-extension/tests/src/test/resources-filtered/projects/project-using-test-parameter-injection/src/main/resources/application.properties @@ -1,3 +1 @@ -quarkus.test.continuous-testing=enabled -# this should not be needed, but something in the tests is setting this to 1234 and confusing the test framework, so set it here to match -quarkus.http.non-application-root-path=1234 \ No newline at end of file +quarkus.test.continuous-testing=enabled \ No newline at end of file diff --git a/integration-tests/test-extension/tests/src/test/resources-filtered/projects/project-using-test-template-from-extension-with-bytecode-changes/src/main/resources/application.properties b/integration-tests/test-extension/tests/src/test/resources-filtered/projects/project-using-test-template-from-extension-with-bytecode-changes/src/main/resources/application.properties index 8d698e657885b..442095ca8410c 100644 --- a/integration-tests/test-extension/tests/src/test/resources-filtered/projects/project-using-test-template-from-extension-with-bytecode-changes/src/main/resources/application.properties +++ b/integration-tests/test-extension/tests/src/test/resources-filtered/projects/project-using-test-template-from-extension-with-bytecode-changes/src/main/resources/application.properties @@ -1,3 +1 @@ -quarkus.test.continuous-testing=enabled -# this should not be needed, but something in the tests is setting this to 1234 and confusing the test framework, so set it here to match -quarkus.http.non-application-root-path=1234 \ No newline at end of file +quarkus.test.continuous-testing=enabled \ No newline at end of file diff --git a/integration-tests/test-extension/tests/src/test/resources-filtered/projects/project-using-test-template-from-extension/src/main/resources/application.properties b/integration-tests/test-extension/tests/src/test/resources-filtered/projects/project-using-test-template-from-extension/src/main/resources/application.properties index 8d698e657885b..442095ca8410c 100644 --- a/integration-tests/test-extension/tests/src/test/resources-filtered/projects/project-using-test-template-from-extension/src/main/resources/application.properties +++ b/integration-tests/test-extension/tests/src/test/resources-filtered/projects/project-using-test-template-from-extension/src/main/resources/application.properties @@ -1,3 +1 @@ -quarkus.test.continuous-testing=enabled -# this should not be needed, but something in the tests is setting this to 1234 and confusing the test framework, so set it here to match -quarkus.http.non-application-root-path=1234 \ No newline at end of file +quarkus.test.continuous-testing=enabled \ No newline at end of file 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 22a4ed3fcca75..63c687909ef0b 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 d5f606290bebe..176751c654bf4 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 0000000000000..9e5a21c6e54bb --- /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/ConfigLauncherSession.java b/test-framework/junit5-config/src/main/java/io/quarkus/test/config/ConfigLauncherSession.java new file mode 100644 index 0000000000000..32a59806500a6 --- /dev/null +++ b/test-framework/junit5-config/src/main/java/io/quarkus/test/config/ConfigLauncherSession.java @@ -0,0 +1,28 @@ +package io.quarkus.test.config; + +import org.eclipse.microprofile.config.spi.ConfigProviderResolver; +import org.junit.platform.launcher.LauncherSession; +import org.junit.platform.launcher.LauncherSessionListener; + +import io.quarkus.runtime.LaunchMode; + +/** + * A JUnit {@link LauncherSessionListener}, used to register the initial test config. Test set up code can safely call + * ConfigProvider.getConfig() to retrieve an instance of the Quarkus configuration. + *

+ * The test config only contains sources known at bootstrap test time. For instance, config sources generated by + * Quarkus are not available in the test config. + */ +public class ConfigLauncherSession implements LauncherSessionListener { + @Override + public void launcherSessionOpened(final LauncherSession session) { + TestConfigProviderResolver resolver = new TestConfigProviderResolver(); + ConfigProviderResolver.setInstance(resolver); + resolver.getConfig(LaunchMode.TEST); + } + + @Override + public void launcherSessionClosed(final LauncherSession session) { + ((TestConfigProviderResolver) ConfigProviderResolver.instance()).restore(); + } +} 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 0000000000000..b15c6f8f4e43f --- /dev/null +++ b/test-framework/junit5-config/src/main/java/io/quarkus/test/config/LoggingSetupExtension.java @@ -0,0 +1,17 @@ +package io.quarkus.test.config; + +import org.junit.jupiter.api.extension.Extension; + +import io.quarkus.runtime.logging.LoggingSetupRecorder; + +/** + * A global JUnit extension 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. + */ +public class LoggingSetupExtension implements Extension { + public LoggingSetupExtension() { + 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 0000000000000..857c13a842548 --- /dev/null +++ b/test-framework/junit5-config/src/main/java/io/quarkus/test/config/QuarkusClassOrderer.java @@ -0,0 +1,41 @@ +package io.quarkus.test.config; + +import org.eclipse.microprofile.config.ConfigProvider; +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.smallrye.config.SmallRyeConfig; + +/** + * A JUnit {@link ClassOrderer}, used to delegate to a custom implementations of {@link ClassOrderer} set by Quarkus + * config. + */ +public class QuarkusClassOrderer implements ClassOrderer { + private final ClassOrderer delegate; + + 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 0000000000000..9bda00bace7f1 --- /dev/null +++ b/test-framework/junit5-config/src/main/java/io/quarkus/test/config/TestConfigProviderResolver.java @@ -0,0 +1,99 @@ +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 org.eclipse.microprofile.config.spi.ConfigProviderResolver; + +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; + +/** + * A {@link org.eclipse.microprofile.config.spi.ConfigProviderResolver} to register {@link Config} in the Test + * classloader. + */ +public class TestConfigProviderResolver extends SmallRyeConfigProviderResolver { + private final SmallRyeConfigProviderResolver resolver; + private final ClassLoader classLoader; + private final Map configs; + + TestConfigProviderResolver() { + this.resolver = (SmallRyeConfigProviderResolver) SmallRyeConfigProviderResolver.instance(); + this.classLoader = Thread.currentThread().getContextClassLoader(); + this.configs = new ConcurrentHashMap<>(); + } + + @Override + public Config getConfig() { + return resolver.getConfig(); + } + + /** + * Registers a config in the Test classloader, by {@link LaunchMode}. Required for tests that launch Quarkus in + * Dev mode (which uses the dev config profile, instead of test. + *

+ * Retrieving the {@link Config} in a {@link LaunchMode} other than {@link LaunchMode#TEST}, must call + * {@link TestConfigProviderResolver#restoreConfig()} after using the config, to avoid mismatches in the config + * profile through the stack. + * + * @param mode the {@link LaunchMode} + * @return the registed {@link Config} instance + */ + 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(); + } + } + + public void restore() { + this.configs.clear(); + ConfigProviderResolver.setInstance(resolver); + } + + @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 0000000000000..a91b69862a876 --- /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/META-INF/services/org.junit.platform.launcher.LauncherSessionListener b/test-framework/junit5-config/src/main/resources/META-INF/services/org.junit.platform.launcher.LauncherSessionListener new file mode 100644 index 0000000000000..ca3c61a72cde2 --- /dev/null +++ b/test-framework/junit5-config/src/main/resources/META-INF/services/org.junit.platform.launcher.LauncherSessionListener @@ -0,0 +1 @@ +io.quarkus.test.config.ConfigLauncherSession 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 0000000000000..d24c3ed5820f5 --- /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 526869cab070b..9bd5cf5b47f47 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 d99d553734bfb..d2718b04f55cf 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; @@ -63,6 +64,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. @@ -234,11 +236,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); } @@ -305,7 +306,7 @@ public void beforeEach(ExtensionContext extensionContext) throws Exception { } @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()); @@ -318,6 +319,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 63fb5bf6fe370..0000000000000 --- 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 cdac134076ffb..0000000000000 --- 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 449f8fda37df5..c4cb92b72454d 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 f8db488299016..5b73fbfaaaf2c 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 1edc18fe9c662..0000000000000 --- 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 97ee658d4e4c0..b3e366e56d849 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 @@ -80,6 +80,7 @@ import io.quarkus.test.junit.callback.QuarkusTestMethodContext; import io.quarkus.test.junit.internal.DeepClone; import io.quarkus.test.junit.internal.NewSerializingDeepClone; +import io.smallrye.config.SmallRyeConfigProviderResolver; public class QuarkusTestExtension extends AbstractJvmQuarkusTestExtension implements BeforeEachCallback, BeforeTestExecutionCallback, AfterTestExecutionCallback, AfterEachCallback, @@ -244,7 +245,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))); @@ -352,7 +355,6 @@ public void beforeTestExecution(ExtensionContext context) throws Exception { } } else { throwBootFailureException(); - return; } } @@ -386,7 +388,6 @@ public void beforeEach(ExtensionContext context) throws Exception { } } else { throwBootFailureException(); - return; } } @@ -1134,21 +1135,20 @@ protected void doClose() { } catch (Throwable e) { log.error("Failed to shutdown Quarkus", e); } finally { + ((SmallRyeConfigProviderResolver) ConfigProviderResolver.instance()) + .releaseConfig(runningQuarkusApplication.getClassLoader()); 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 e3ec91cd40b6a..54580344058a4 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/java/io/quarkus/test/junit/util/QuarkusTestProfileAwareClassOrderer.java b/test-framework/junit5/src/main/java/io/quarkus/test/junit/util/QuarkusTestProfileAwareClassOrderer.java index 9607d14b5f7d7..a3d13b02f0436 100644 --- a/test-framework/junit5/src/main/java/io/quarkus/test/junit/util/QuarkusTestProfileAwareClassOrderer.java +++ b/test-framework/junit5/src/main/java/io/quarkus/test/junit/util/QuarkusTestProfileAwareClassOrderer.java @@ -6,6 +6,8 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; +import org.eclipse.microprofile.config.Config; +import org.eclipse.microprofile.config.ConfigProvider; import org.junit.jupiter.api.ClassDescriptor; import org.junit.jupiter.api.ClassOrderer; import org.junit.jupiter.api.ClassOrdererContext; @@ -27,7 +29,7 @@ *

* By default, Quarkus*Tests not using any profile come first, then classes using a profile (in groups) and then all other * non-Quarkus tests (e.g. plain unit tests).
- * Quarkus*Tests with {@linkplain WithTestResource#restrictToAnnotatedClass()} or + * Quarkus*Tests with {@linkplain WithTestResource#scope() matching resources} or * {@linkplain QuarkusTestResource#restrictToAnnotatedClass() restricted} {@code QuarkusTestResource} come * after tests with profiles and Quarkus*Tests with only unrestricted resources are handled like tests without a profile (come * first). @@ -35,7 +37,7 @@ * Internally, ordering is based on prefixes that are prepended to a secondary order suffix (by default the fully qualified * name of the respective test class), with the fully qualified class name of the * {@link io.quarkus.test.junit.QuarkusTestProfile QuarkusTestProfile} as an infix (if present). - * The default prefixes are defined by {@code DEFAULT_ORDER_PREFIX_*} and can be overridden in {@code junit-platform.properties} + * The default prefixes are defined by {@code DEFAULT_ORDER_PREFIX_*} and can be overridden in {@code application.properties} * via {@code CFGKEY_ORDER_PREFIX_*}, e.g. non-Quarkus tests can be run first (not last) by setting * {@link #CFGKEY_ORDER_PREFIX_NON_QUARKUS_TEST} to {@code 10_}. *

@@ -69,31 +71,57 @@ public class QuarkusTestProfileAwareClassOrderer implements ClassOrderer { static final String CFGKEY_SECONDARY_ORDERER = "junit.quarkus.orderer.secondary-orderer"; + private final String prefixQuarkusTest; + private final String prefixQuarkusTestWithProfile; + private final String prefixQuarkusTestWithRestrictedResource; + private final String prefixNonQuarkusTest; + private final Optional secondaryOrderer; + + public QuarkusTestProfileAwareClassOrderer() { + Config config = ConfigProvider.getConfig(); + this.prefixQuarkusTest = config.getOptionalValue(CFGKEY_ORDER_PREFIX_QUARKUS_TEST, String.class) + .orElse(DEFAULT_ORDER_PREFIX_QUARKUS_TEST); + this.prefixQuarkusTestWithProfile = config.getOptionalValue(CFGKEY_ORDER_PREFIX_QUARKUS_TEST_WITH_PROFILE, String.class) + .orElse(DEFAULT_ORDER_PREFIX_QUARKUS_TEST_WITH_PROFILE); + this.prefixQuarkusTestWithRestrictedResource = config + .getOptionalValue(CFGKEY_ORDER_PREFIX_QUARKUS_TEST_WITH_RESTRICTED_RES, String.class) + .orElse(DEFAULT_ORDER_PREFIX_QUARKUS_TEST_WITH_RESTRICTED_RES); + this.prefixNonQuarkusTest = config.getOptionalValue(CFGKEY_ORDER_PREFIX_NON_QUARKUS_TEST, String.class) + .orElse(DEFAULT_ORDER_PREFIX_NON_QUARKUS_TEST); + this.secondaryOrderer = config.getOptionalValue(CFGKEY_SECONDARY_ORDERER, String.class); + } + + QuarkusTestProfileAwareClassOrderer( + final String prefixQuarkusTest, + final String prefixQuarkusTestWithProfile, + final String prefixQuarkusTestWithRestrictedResource, + final String prefixNonQuarkusTest, + final Optional secondaryOrderer) { + this.prefixQuarkusTest = prefixQuarkusTest; + this.prefixQuarkusTestWithProfile = prefixQuarkusTestWithProfile; + this.prefixQuarkusTestWithRestrictedResource = prefixQuarkusTestWithRestrictedResource; + this.prefixNonQuarkusTest = prefixNonQuarkusTest; + this.secondaryOrderer = secondaryOrderer; + } + @Override public void orderClasses(ClassOrdererContext context) { // don't do anything if there is just one test class or the current order request is for @Nested tests if (context.getClassDescriptors().size() <= 1 || context.getClassDescriptors().get(0).isAnnotated(Nested.class)) { return; } - var prefixQuarkusTest = getConfigParam( - CFGKEY_ORDER_PREFIX_QUARKUS_TEST, - DEFAULT_ORDER_PREFIX_QUARKUS_TEST, - context); - var prefixQuarkusTestWithProfile = getConfigParam( - CFGKEY_ORDER_PREFIX_QUARKUS_TEST_WITH_PROFILE, - DEFAULT_ORDER_PREFIX_QUARKUS_TEST_WITH_PROFILE, - context); - var prefixQuarkusTestWithRestrictedResource = getConfigParam( - CFGKEY_ORDER_PREFIX_QUARKUS_TEST_WITH_RESTRICTED_RES, - DEFAULT_ORDER_PREFIX_QUARKUS_TEST_WITH_RESTRICTED_RES, - context); - var prefixNonQuarkusTest = getConfigParam( - CFGKEY_ORDER_PREFIX_NON_QUARKUS_TEST, - DEFAULT_ORDER_PREFIX_NON_QUARKUS_TEST, - context); // first pass: run secondary orderer first (!), which is easier than running it per "grouping" - buildSecondaryOrderer(context).orderClasses(context); + secondaryOrderer + .map(fqcn -> { + try { + return (ClassOrderer) Class.forName(fqcn).getDeclaredConstructor().newInstance(); + } catch (ReflectiveOperationException e) { + throw new IllegalArgumentException("Failed to instantiate " + fqcn, e); + } + }) + .orElseGet(ClassName::new).orderClasses(context); + var classDecriptors = context.getClassDescriptors(); var firstPassIndexMap = IntStream.range(0, classDecriptors.size()).boxed() .collect(Collectors.toMap(classDecriptors::get, i -> String.format("%06d", i))); @@ -123,22 +151,6 @@ public void orderClasses(ClassOrdererContext context) { })); } - private String getConfigParam(String key, String fallbackValue, ClassOrdererContext context) { - return context.getConfigurationParameter(key).orElse(fallbackValue); - } - - private ClassOrderer buildSecondaryOrderer(ClassOrdererContext context) { - return Optional.ofNullable(getConfigParam(CFGKEY_SECONDARY_ORDERER, null, context)) - .map(fqcn -> { - try { - return (ClassOrderer) Class.forName(fqcn).getDeclaredConstructor().newInstance(); - } catch (ReflectiveOperationException e) { - throw new IllegalArgumentException("Failed to instantiate " + fqcn, e); - } - }) - .orElseGet(ClassOrderer.ClassName::new); - } - private boolean hasRestrictedResource(ClassDescriptor classDescriptor) { return classDescriptor.findRepeatableAnnotations(WithTestResource.class).stream() .anyMatch( 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 3d466fc6d2db5..0000000000000 --- 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/test/java/io/quarkus/test/junit/util/QuarkusTestProfileAwareClassOrdererTest.java b/test-framework/junit5/src/test/java/io/quarkus/test/junit/util/QuarkusTestProfileAwareClassOrdererTest.java index acb5f2cf5b834..d91035f34f311 100644 --- a/test-framework/junit5/src/test/java/io/quarkus/test/junit/util/QuarkusTestProfileAwareClassOrdererTest.java +++ b/test-framework/junit5/src/test/java/io/quarkus/test/junit/util/QuarkusTestProfileAwareClassOrdererTest.java @@ -1,7 +1,5 @@ package io.quarkus.test.junit.util; -import static io.quarkus.test.junit.util.QuarkusTestProfileAwareClassOrderer.CFGKEY_ORDER_PREFIX_NON_QUARKUS_TEST; -import static io.quarkus.test.junit.util.QuarkusTestProfileAwareClassOrderer.CFGKEY_SECONDARY_ORDERER; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doReturn; @@ -40,14 +38,12 @@ class QuarkusTestProfileAwareClassOrdererTest { @Mock ClassOrdererContext contextMock; - QuarkusTestProfileAwareClassOrderer underTest = new QuarkusTestProfileAwareClassOrderer(); - @Test void singleClass() { doReturn(Arrays.asList(descriptorMock(Test01.class))) .when(contextMock).getClassDescriptors(); - underTest.orderClasses(contextMock); + new QuarkusTestProfileAwareClassOrderer().orderClasses(contextMock); verify(contextMock, never()).getConfigurationParameter(anyString()); } @@ -89,7 +85,7 @@ void allVariants() { quarkusTestWithUnrestrictedQuarkusTestResourceDesc); doReturn(input).when(contextMock).getClassDescriptors(); - underTest.orderClasses(contextMock); + new QuarkusTestProfileAwareClassOrderer().orderClasses(contextMock); assertThat(input).containsExactly( quarkusTest1Desc, @@ -114,11 +110,7 @@ void configuredPrefix() { List input = Arrays.asList(quarkusTestDesc, nonQuarkusTestDesc); doReturn(input).when(contextMock).getClassDescriptors(); - when(contextMock.getConfigurationParameter(anyString())).thenReturn(Optional.empty()); // for strict stubbing - // prioritize unit tests - when(contextMock.getConfigurationParameter(CFGKEY_ORDER_PREFIX_NON_QUARKUS_TEST)).thenReturn(Optional.of("01_")); - - underTest.orderClasses(contextMock); + new QuarkusTestProfileAwareClassOrderer("20_", "40_", "45_", "01_", Optional.empty()).orderClasses(contextMock); assertThat(input).containsExactly(nonQuarkusTestDesc, quarkusTestDesc); } @@ -137,12 +129,8 @@ void secondaryOrderer() { quarkusTest1Desc); doReturn(input).when(contextMock).getClassDescriptors(); - when(contextMock.getConfigurationParameter(anyString())).thenReturn(Optional.empty()); // for strict stubbing - // change secondary orderer from ClassName to OrderAnnotation - when(contextMock.getConfigurationParameter(CFGKEY_SECONDARY_ORDERER)) - .thenReturn(Optional.of(ClassOrderer.OrderAnnotation.class.getName())); - - underTest.orderClasses(contextMock); + new QuarkusTestProfileAwareClassOrderer("20_", "40_", "45_", "60_", + Optional.of(ClassOrderer.OrderAnnotation.class.getName())).orderClasses(contextMock); assertThat(input).containsExactly( quarkusTest1Desc, @@ -157,14 +145,13 @@ void customOrderKey() { List input = Arrays.asList(quarkusTest1Desc, quarkusTest2Desc); doReturn(input).when(contextMock).getClassDescriptors(); - underTest = new QuarkusTestProfileAwareClassOrderer() { + new QuarkusTestProfileAwareClassOrderer() { @Override protected Optional getCustomOrderKey(ClassDescriptor classDescriptor, ClassOrdererContext context, String secondaryOrderSuffix) { return classDescriptor == quarkusTest2Desc ? Optional.of("00_first") : Optional.empty(); } - }; - underTest.orderClasses(contextMock); + }.orderClasses(contextMock); assertThat(input).containsExactly(quarkusTest2Desc, quarkusTest1Desc); } diff --git a/test-framework/pom.xml b/test-framework/pom.xml index 888a8dd222928..fef1a28244cb7 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