Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Register a Config instance to bootstrap tests #42715

Merged
merged 1 commit into from
Dec 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion bom/application/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3284,7 +3284,7 @@
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5-properties</artifactId>
<artifactId>quarkus-junit5-config</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -45,6 +47,16 @@ public interface TestConfig {
@WithDefault("false")
boolean displayTestOutput();

/**
* The FQCN of the JUnit <code>ClassOrderer</code> to use. If the class cannot be found, it fallbacks to JUnit
* default behaviour which does not set a <code>ClassOrderer</code> at all.
*
* @see <a href=https://junit.org/junit5/docs/current/user-guide/#writing-tests-test-execution-order-classes>JUnit Class
* Order<a/>
*/
@WithDefault("io.quarkus.test.junit.util.QuarkusTestProfileAwareClassOrderer")
Optional<String> classOrderer();
radcortez marked this conversation as resolved.
Show resolved Hide resolved

/**
* Tags that should be included for continuous testing. This supports JUnit Tag Expressions.
*
Expand Down Expand Up @@ -77,7 +89,6 @@ public interface TestConfig {
* is matched against the test class name (not the file name).
* <p>
* This is ignored if include-pattern has been set.
*
*/
@WithDefault(".*\\.IT[^.]+|.*IT|.*ITCase")
Optional<String> excludePattern();
Expand Down Expand Up @@ -241,7 +252,6 @@ public interface TestConfig {
* is matched against the module groupId:artifactId.
* <p>
* This is ignored if include-module-pattern has been set.
*
*/
Optional<String> excludeModulePattern();

Expand All @@ -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<List<String>> tags();
Optional<List<@WithConverter(TrimmedStringConverter.class) String>> tags();
}

interface Container {
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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) {
Expand All @@ -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;
Expand Down
22 changes: 4 additions & 18 deletions docs/src/main/asciidoc/getting-started-testing.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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]
----
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5-properties</artifactId>
</exclusion>
</exclusions>
</dependency>
----
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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<ResolveContext<RuntimeException>, StringBuilder>() {
@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<ResolveContext<RuntimeException>, StringBuilder>() {
@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -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
quarkus.test.continuous-testing=enabled
Original file line number Diff line number Diff line change
@@ -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
quarkus.test.continuous-testing=enabled
Original file line number Diff line number Diff line change
@@ -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
quarkus.test.continuous-testing=enabled
Original file line number Diff line number Diff line change
@@ -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
quarkus.test.continuous-testing=enabled
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand All @@ -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();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,6 @@
import org.jboss.jandex.DotName;
import org.jboss.jandex.IndexView;

import io.smallrye.config.SmallRyeConfigProviderResolver;

/**
* Manages {@link QuarkusTestResourceLifecycleManager}
*/
Expand Down Expand Up @@ -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();
}

Expand Down
35 changes: 35 additions & 0 deletions test-framework/junit5-config/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-test-framework</artifactId>
<version>999-SNAPSHOT</version>
</parent>

<artifactId>quarkus-junit5-config</artifactId>
<name>Quarkus - Test Framework - JUnit 5 Config</name>

<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
</dependency>
<dependency>
<groupId>io.smallrye.config</groupId>
<artifactId>smallrye-config</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-core</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-core-deployment</artifactId>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -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
* <code>ConfigProvider.getConfig()</code> to retrieve an instance of the Quarkus configuration.
* <p>
* 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();
}
}
Original file line number Diff line number Diff line change
@@ -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.
* <p/>
* 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();
}
}
Original file line number Diff line number Diff line change
@@ -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) {

}
};
}
Loading
Loading