Skip to content

Commit

Permalink
Merge pull request #3332 from aloubyansky/gradle-project-test
Browse files Browse the repository at this point in the history
Support running Quarkus tests from Gradle
  • Loading branch information
aloubyansky authored Aug 11, 2019
2 parents 2f5b6c7 + 1d57223 commit 0d98c45
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 <a href="mailto:[email protected]">Ståle Pedersen</a>
Expand Down Expand Up @@ -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() {
Expand Down
Original file line number Diff line number Diff line change
@@ -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<AppDependency> 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<String, Object> 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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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<URL> 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();
Expand Down

0 comments on commit 0d98c45

Please sign in to comment.