diff --git a/devtools/gradle/src/main/java/io/quarkus/gradle/QuarkusPlugin.java b/devtools/gradle/src/main/java/io/quarkus/gradle/QuarkusPlugin.java
index 67b74acdc1be7..555277471721d 100644
--- a/devtools/gradle/src/main/java/io/quarkus/gradle/QuarkusPlugin.java
+++ b/devtools/gradle/src/main/java/io/quarkus/gradle/QuarkusPlugin.java
@@ -7,6 +7,7 @@
import org.gradle.api.plugins.BasePlugin;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.tasks.TaskContainer;
+import org.gradle.api.tasks.testing.Test;
import org.gradle.util.GradleVersion;
import io.quarkus.gradle.tasks.QuarkusAddExtension;
@@ -15,6 +16,7 @@
import io.quarkus.gradle.tasks.QuarkusGenerateConfig;
import io.quarkus.gradle.tasks.QuarkusListExtensions;
import io.quarkus.gradle.tasks.QuarkusNative;
+import io.quarkus.gradle.tasks.QuarkusTestConfig;
/**
* @author Ståle Pedersen
@@ -51,6 +53,10 @@ private void registerTasks(Project project) {
});
tasks.create("buildNative", QuarkusNative.class).dependsOn(quarkusBuild);
+
+ // Quarkus test configuration task which should be executed before any Quarkus test
+ final QuarkusTestConfig quarkusTestConfig = tasks.create("quarkusTestConfig", QuarkusTestConfig.class);
+ tasks.withType(Test.class).forEach(t -> t.dependsOn(quarkusTestConfig));
}
private void verifyGradleVersion() {
diff --git a/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusTestConfig.java b/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusTestConfig.java
new file mode 100644
index 0000000000000..459de25f8dec8
--- /dev/null
+++ b/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusTestConfig.java
@@ -0,0 +1,44 @@
+package io.quarkus.gradle.tasks;
+
+import java.util.List;
+import java.util.Map;
+
+import org.gradle.api.tasks.TaskAction;
+import org.gradle.api.tasks.testing.Test;
+
+import io.quarkus.bootstrap.BootstrapClassLoaderFactory;
+import io.quarkus.bootstrap.model.AppDependency;
+import io.quarkus.gradle.QuarkusPluginExtension;
+
+public class QuarkusTestConfig extends QuarkusTask {
+
+ public QuarkusTestConfig() {
+ super("Sets the necessary system properties for the Quarkus tests to run.");
+ }
+
+ @TaskAction
+ public void setupTest() {
+ final QuarkusPluginExtension quarkusExt = extension();
+ try {
+ final List deploymentDeps = quarkusExt.resolveAppModel().resolveModel(quarkusExt.getAppArtifact())
+ .getDeploymentDependencies();
+ final StringBuilder buf = new StringBuilder();
+ for (AppDependency dep : deploymentDeps) {
+ buf.append(dep.getArtifact().getPath().toUri().toURL().toExternalForm());
+ buf.append(' ');
+ }
+ final String deploymentCp = buf.toString();
+ final String nativeRunner = getProject().getBuildDir().toPath().resolve(quarkusExt.finalName() + "-runner")
+ .toAbsolutePath()
+ .toString();
+
+ for (Test test : getProject().getTasks().withType(Test.class)) {
+ final Map props = test.getSystemProperties();
+ props.put(BootstrapClassLoaderFactory.PROP_DEPLOYMENT_CP, deploymentCp);
+ props.put("native.image.path", nativeRunner);
+ }
+ } catch (Exception e) {
+ throw new IllegalStateException("Failed to resolve deployment classpath", e);
+ }
+ }
+}
diff --git a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/BootstrapClassLoaderFactory.java b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/BootstrapClassLoaderFactory.java
index 0bccbb9171bce..47ba356641da1 100644
--- a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/BootstrapClassLoaderFactory.java
+++ b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/BootstrapClassLoaderFactory.java
@@ -35,8 +35,9 @@ public class BootstrapClassLoaderFactory {
private static final String DEPLOYMENT_CP = "deployment.cp";
public static final String PROP_CP_CACHE = "quarkus-classpath-cache";
- public static final String PROP_WS_DISCOVERY = "quarkus-workspace-discovery";
+ public static final String PROP_DEPLOYMENT_CP = "quarkus-deployment-cp";
public static final String PROP_OFFLINE = "quarkus-bootstrap-offline";
+ public static final String PROP_WS_DISCOVERY = "quarkus-workspace-discovery";
private static final int CP_CACHE_FORMAT_ID = 1;
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 c122b7898cbd1..7c9fbb9ce8a61 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
@@ -7,11 +7,13 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
@@ -73,18 +75,7 @@ private ExtensionState doJavaStart(ExtensionContext context, TestResourceManager
Path appClassLocation = getAppClassLocation(context.getRequiredTestClass());
- try {
- appCl = BootstrapClassLoaderFactory.newInstance()
- .setAppClasses(appClassLocation)
- .setParent(getClass().getClassLoader())
- .setOffline(PropertyUtils.getBooleanOrNull(BootstrapClassLoaderFactory.PROP_OFFLINE))
- .setLocalProjectsDiscovery(
- PropertyUtils.getBoolean(BootstrapClassLoaderFactory.PROP_WS_DISCOVERY, true))
- .setEnableClasspathCache(PropertyUtils.getBoolean(BootstrapClassLoaderFactory.PROP_CP_CACHE, true))
- .newDeploymentClassLoader();
- } catch (BootstrapException e) {
- throw new IllegalStateException("Failed to create the boostrap class loader", e);
- }
+ appCl = createQuarkusBuildClassLoader(appClassLocation);
originalCl = setCCL(appCl);
final Path testClassLocation = getTestClassesLocation(context.getRequiredTestClass());
@@ -262,6 +253,47 @@ public void run() {
return new ExtensionState(testResourceManager, shutdownTask, false);
}
+ /**
+ * Creates a classloader that will be used to build the test application.
+ *
+ * This method assumes that the runtime classes are already on the classpath
+ * of the classloader that loaded this class.
+ * What this method does is it resolves the required deployment classpath
+ * and creates a new URL classloader that includes the deployment CP with
+ * the classloader that loaded this class as its parent.
+ *
+ * @param appClassLocation location of the test application classes
+ * @return application build classloader
+ */
+ private URLClassLoader createQuarkusBuildClassLoader(Path appClassLocation) {
+ // The deployment classpath could be passed in as a system property.
+ // This is how integration with the Gradle plugin is achieved.
+ final String deploymentCp = PropertyUtils.getProperty(BootstrapClassLoaderFactory.PROP_DEPLOYMENT_CP);
+ if (deploymentCp != null && !deploymentCp.isEmpty()) {
+ final List list = new ArrayList<>();
+ for (String entry : deploymentCp.split("\\s")) {
+ try {
+ list.add(new URL(entry));
+ } catch (MalformedURLException e) {
+ throw new IllegalStateException("Failed to parse a deployment classpath entry " + entry, e);
+ }
+ }
+ return new URLClassLoader(list.toArray(new URL[list.size()]), getClass().getClassLoader());
+ }
+ try {
+ return BootstrapClassLoaderFactory.newInstance()
+ .setAppClasses(appClassLocation)
+ .setParent(getClass().getClassLoader())
+ .setOffline(PropertyUtils.getBooleanOrNull(BootstrapClassLoaderFactory.PROP_OFFLINE))
+ .setLocalProjectsDiscovery(
+ PropertyUtils.getBoolean(BootstrapClassLoaderFactory.PROP_WS_DISCOVERY, true))
+ .setEnableClasspathCache(PropertyUtils.getBoolean(BootstrapClassLoaderFactory.PROP_CP_CACHE, true))
+ .newDeploymentClassLoader();
+ } catch (BootstrapException e) {
+ throw new IllegalStateException("Failed to create the boostrap class loader", e);
+ }
+ }
+
@Override
public void afterEach(ExtensionContext context) throws Exception {
restAssuredURLManager.clearURL();