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

Enhance the platform Maven plugin configuration to configure a test, added an integration test for the Maven plugin #352

Merged
merged 1 commit into from
Oct 2, 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
13 changes: 10 additions & 3 deletions integration-tests/sample-app/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-bootstrap-app-model</artifactId>
<version>${quarkus.version}</version>
<artifactId>quarkus-bootstrap-maven-resolver</artifactId>
<scope>test</scope>
</dependency>
<dependency>
Expand Down Expand Up @@ -41,7 +40,7 @@
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>${surefire-plugin.version}</version>
<configuration>
<systemPropertyVariables>
Expand All @@ -52,6 +51,14 @@
<quarkus.platform.version>${quarkus.version}</quarkus.platform.version>
</systemPropertyVariables>
</configuration>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,286 @@
package io.quarkus.platform.test;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.fail;

import io.quarkus.bootstrap.resolver.maven.BootstrapMavenContext;
import io.quarkus.bootstrap.resolver.maven.MavenArtifactResolver;
import java.io.File;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.collection.CollectRequest;
import org.eclipse.aether.graph.Dependency;
import org.eclipse.aether.repository.WorkspaceReader;
import org.eclipse.aether.repository.WorkspaceRepository;
import org.eclipse.aether.resolution.DependencyRequest;
import org.jboss.logging.Logger;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

/**
* This test generates, builds and tests a sample Quarkus application (unit and integration tests).
*/
public class SampleProjectGenerationIT {

private static final Logger log = Logger.getLogger(SampleProjectGenerationIT.class);

private static final String SAMPLE_APP = "sample-app";
private static final String GENERATE_APP_TIMEOUT = "sample-app.generate-timeout";
private static final String BUILD_APP_TIMEOUT = "sample-app.build-timeout";

private static String PLUGIN_GROUP_ID;
private static String PLUGIN_VERSION;
private static Path workDir;
private static Path pluginRepoDir;

@BeforeAll
public static void preinstallPlugin() throws Exception {

PLUGIN_GROUP_ID = getRequiredProperty("quarkus.platform.group-id");
PLUGIN_VERSION = getRequiredProperty("quarkus.platform.version");

workDir = Path.of("target/plugin-test").toAbsolutePath();
recursiveDelete(workDir);
pluginRepoDir = workDir.resolve("maven-repo");

installLocalPlugin();
}

/**
* If the target Maven plugin is a part of the project, this method will install the Maven plugin and its relevant
* dependencies
* into a temporary local repository to make them resolvable from the test.
*
* @throws Exception in case anything goes wrong
*/
private static void installLocalPlugin() throws Exception {
final BootstrapMavenContext mvnCtx = new BootstrapMavenContext();
var project = mvnCtx.getCurrentProject();
if (project == null) {
return;
}

var session = new DefaultRepositorySystemSession(mvnCtx.getRepositorySystemSession());
final List<Artifact> artifactsToInstall = new ArrayList<>();
session.setWorkspaceReader(new WorkspaceReader() {
final WorkspaceRepository repo = new WorkspaceRepository("workspace");

@Override
public WorkspaceRepository getRepository() {
return repo;
}

@Override
public File findArtifact(Artifact artifact) {
final File result = project.getWorkspace().findArtifact(artifact);
if (result != null) {
artifactsToInstall.add(new DefaultArtifact(artifact.getGroupId(), artifact.getArtifactId(),
artifact.getClassifier(), artifact.getExtension(), artifact.getVersion(), Map.of(),
result));
}
return result;
}

@Override
public List<String> findVersions(Artifact artifact) {
return project.getWorkspace().findVersions(artifact);
}
});

mvnCtx.getRepositorySystem().resolveDependencies(session,
new DependencyRequest().setCollectRequest(
new CollectRequest(
new Dependency(
new DefaultArtifact(PLUGIN_GROUP_ID, "quarkus-maven-plugin", "jar", PLUGIN_VERSION),
"compile"),
mvnCtx.getRemoteRepositories())));

if (!artifactsToInstall.isEmpty()) {
var installer = MavenArtifactResolver.builder()
.setLocalRepository(pluginRepoDir.toString())
.setRepositorySystem(mvnCtx.getRepositorySystem())
.setRemoteRepositories(mvnCtx.getRemoteRepositories())
.setRemoteRepositoryManager(mvnCtx.getRemoteRepositoryManager())
.setCurrentProject(project)
.build();
for (var a : artifactsToInstall) {
installer.install(a);
}
}
}

@Test
public void testSampleProjectGeneration() throws Exception {
// create a sample project
final Path projectDir = generateSampleAppWithMaven();
// build and test the sample project
buildApp(projectDir);
}

private static void buildApp(Path projectDir) throws IOException {

final Path cliLog = projectDir.resolve("build.log");
final Path cliErrors = projectDir.resolve("build-errors.log");
final long startTime = System.currentTimeMillis();
Process process = new ProcessBuilder()
.directory(projectDir.toFile())
.command(isWindows() ? "mvnw" : "./mvnw", "verify", "-DskipITs=false",
"-Dmaven.repo.local.tail=" + pluginRepoDir)
.redirectOutput(cliLog.toFile())
.redirectError(cliErrors.toFile())
.start();
log.info("Building the application with '" + process.info().commandLine().orElse("<command not available>") + "'");
try {
if (!process.waitFor(getBuildAppTimeout(), TimeUnit.SECONDS)) {
try {
process.destroy();
} catch (Exception e) {
log.error("Failed to destroy the process", e);
}
fail("The process has exceeded the time out limit of " + getBuildAppTimeout() + " seconds");
}
} catch (InterruptedException e) {
log.error("Interrupted waiting for the process to complete", e);
}
process = process.onExit().join();
log.infof("Done in %.1f seconds", ((double) (System.currentTimeMillis() - startTime)) / 1000);

if (process.exitValue() != 0) {
var message = Files.readString(cliErrors);
if (message.isEmpty()) {
fail(Files.readString(cliLog));
}
fail(Files.readString(cliErrors));
}
}

private static String getMvnPath() {
var mavenHome = System.getProperty("maven.home");
if (mavenHome == null) {
return "mvn";
}
return Path.of(mavenHome).resolve("bin").resolve(isWindows() ? "mvn.bat" : "mvn").toString();
}

public static boolean isWindows() {
return System.getProperty("os.name").toLowerCase(Locale.ENGLISH).contains("windows");
}

private static Path generateSampleAppWithMaven() throws IOException {

Files.createDirectories(workDir);
final Path cliLog = workDir.resolve("cli.log");
final Path cliErrors = workDir.resolve("cli-errors.log");
final long startTime = System.currentTimeMillis();
Process process = new ProcessBuilder()
.directory(workDir.toFile())
.command(getMvnPath(),
PLUGIN_GROUP_ID + ":quarkus-maven-plugin:" + PLUGIN_VERSION + ":create",
"-DplatformGroupId=" + PLUGIN_GROUP_ID,
"-DplatformVersion=" + PLUGIN_VERSION,
"-DprojectGroupId=org.acme",
"-DprojectArtifactId=" + SAMPLE_APP,
"-DprojectVersion=1.0-SNAPSHOT",
"-DquarkusRegistryClient=false",
"-Dmaven.repo.local.tail=" + pluginRepoDir)
.redirectOutput(cliLog.toFile())
.redirectError(cliErrors.toFile())
.start();
log.info("Generating application with '" + process.info().commandLine().orElse("<command not available>") + "'");
try {
if (!process.waitFor(getGenerateAppTimeout(), TimeUnit.SECONDS)) {
try {
process.destroy();
} catch (Exception e) {
log.error("Failed to destroy the process", e);
}
fail("The process has exceeded the time out limit of " + getGenerateAppTimeout() + " seconds");
}
} catch (InterruptedException e) {
log.error("Interrupted waiting for the process to complete", e);
}
process = process.onExit().join();
log.infof("Done in %.1f seconds", ((double) (System.currentTimeMillis() - startTime)) / 1000);

if (process.exitValue() != 0) {
var message = Files.readString(cliErrors);
if (message.isEmpty()) {
fail(Files.readString(cliLog));
}
fail(Files.readString(cliErrors));
}
var projectDir = workDir.resolve(SAMPLE_APP);
assertThat(projectDir).exists();
return projectDir;
}

private static String getRequiredProperty(String name) {
var value = System.getProperty(name);
if (value == null) {
fail("Required property " + name + " is not set");
}
return value;
}

private static long getBuildAppTimeout() {
var propValue = System.getProperty(BUILD_APP_TIMEOUT);
if (propValue == null) {
return 30;
}
return Long.parseLong(propValue);
}

private static long getGenerateAppTimeout() {
var propValue = System.getProperty(GENERATE_APP_TIMEOUT);
if (propValue == null) {
return 10;
}
return Long.parseLong(propValue);
}

public static void recursiveDelete(Path root) {
if (root == null || !Files.exists(root)) {
return;
}
try {
Files.walkFileTree(root, new SimpleFileVisitor<>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
try {
Files.delete(file);
} catch (IOException ex) {
}
return FileVisitResult.CONTINUE;
}

@Override
public FileVisitResult postVisitDirectory(Path dir, IOException e)
throws IOException {
if (e == null) {
try {
Files.delete(dir);
} catch (IOException ex) {
}
return FileVisitResult.CONTINUE;
} else {
// directory iteration failed
throw e;
}
}
});
} catch (IOException e) {
}
}
}
Loading
Loading