diff --git a/independent-projects/bootstrap/bom-test/pom.xml b/independent-projects/bootstrap/bom-test/pom.xml
index 2925446821caa7..75ceb03f4c8062 100644
--- a/independent-projects/bootstrap/bom-test/pom.xml
+++ b/independent-projects/bootstrap/bom-test/pom.xml
@@ -39,6 +39,30 @@
test
${shrinkwrap-depchain.version}
+
+ org.apache.maven.plugin-testing
+ maven-plugin-testing-harness
+ 3.3.0
+ test
+
+
+
+ org.codehaus.plexus
+ plexus-utils
+
+
+ commons-io
+ commons-io
+
+
+
+
+
+ org.apache.maven
+ maven-compat
+ ${maven.version}
+ test
+
diff --git a/independent-projects/bootstrap/maven-plugin/src/main/java/io/quarkus/maven/ExtensionDescriptorMojo.java b/independent-projects/bootstrap/maven-plugin/src/main/java/io/quarkus/maven/ExtensionDescriptorMojo.java
index 23f8a2eaead13d..47f8d83436818d 100644
--- a/independent-projects/bootstrap/maven-plugin/src/main/java/io/quarkus/maven/ExtensionDescriptorMojo.java
+++ b/independent-projects/bootstrap/maven-plugin/src/main/java/io/quarkus/maven/ExtensionDescriptorMojo.java
@@ -201,7 +201,7 @@ public static class RemovedResources {
@Parameter(required = false, defaultValue = "${skipExtensionValidation}")
private boolean skipExtensionValidation;
- @Parameter(required = false, defaultValue = "${ignoreNotDetectedQuarkusCoreVersion")
+ @Parameter(required = false, defaultValue = "${ignoreNotDetectedQuarkusCoreVersion}")
boolean ignoreNotDetectedQuarkusCoreVersion;
@Parameter
diff --git a/independent-projects/extension-maven-plugin/pom.xml b/independent-projects/extension-maven-plugin/pom.xml
index 5f4b87169537c0..e1170124f2f524 100644
--- a/independent-projects/extension-maven-plugin/pom.xml
+++ b/independent-projects/extension-maven-plugin/pom.xml
@@ -39,6 +39,12 @@
5.9.0
+
+
+ src/test/resources
+ true
+
+
@@ -48,6 +54,20 @@
none
+
+
+ maven-clean-plugin
+
+
+
+ src/test/resources
+
+ **/target/**
+
+
+
+
+
org.apache.maven.plugins
maven-enforcer-plugin
@@ -188,6 +208,13 @@
pom
import
+
+ io.quarkus
+ quarkus-bootstrap-bom-test
+ ${project.version}
+ pom
+ import
+
com.fasterxml.jackson
@@ -196,6 +223,7 @@
import
pom
+
@@ -238,6 +266,29 @@
${junit.jupiter.version}
test
+
+ org.apache.maven.plugin-testing
+ maven-plugin-testing-harness
+ test
+
+
+
+ org.apache.maven
+ maven-compat
+ test
+
+
+ org.apache.maven.shared
+ maven-invoker
+ 3.2.0
+ test
+
+
+ javax.inject
+ javax.inject
+
+
+
diff --git a/independent-projects/extension-maven-plugin/src/main/java/io/quarkus/maven/ExtensionDescriptorMojo.java b/independent-projects/extension-maven-plugin/src/main/java/io/quarkus/maven/ExtensionDescriptorMojo.java
index b042d97ee30c65..983f3faab78382 100644
--- a/independent-projects/extension-maven-plugin/src/main/java/io/quarkus/maven/ExtensionDescriptorMojo.java
+++ b/independent-projects/extension-maven-plugin/src/main/java/io/quarkus/maven/ExtensionDescriptorMojo.java
@@ -103,7 +103,7 @@ public static class RemovedResources {
* @component
*/
@Component
- private RepositorySystem repoSystem;
+ RepositorySystem repoSystem;
@Component
RemoteRepositoryManager remoteRepoManager;
@@ -121,7 +121,7 @@ public static class RemovedResources {
* @readonly
*/
@Parameter(defaultValue = "${repositorySystemSession}", readonly = true)
- private RepositorySystemSession repoSession;
+ RepositorySystemSession repoSession;
/**
* The project's remote repositories to use for the resolution of artifacts and
@@ -213,7 +213,7 @@ public static class RemovedResources {
* Whether to ignore failure detecting the Quarkus core version used to build the extension,
* which would be recorded in the extension's metadata.
*/
- @Parameter(required = false, defaultValue = "${ignoreNotDetectedQuarkusCoreVersion")
+ @Parameter(required = false, defaultValue = "${ignoreNotDetectedQuarkusCoreVersion}")
boolean ignoreNotDetectedQuarkusCoreVersion;
/**
diff --git a/independent-projects/extension-maven-plugin/src/test/java/io/quarkus/maven/ExtensionDescriptorMojoTest.java b/independent-projects/extension-maven-plugin/src/test/java/io/quarkus/maven/ExtensionDescriptorMojoTest.java
index 53bb40eefdceaa..0c63ac936ed9a7 100644
--- a/independent-projects/extension-maven-plugin/src/test/java/io/quarkus/maven/ExtensionDescriptorMojoTest.java
+++ b/independent-projects/extension-maven-plugin/src/test/java/io/quarkus/maven/ExtensionDescriptorMojoTest.java
@@ -1,14 +1,181 @@
package io.quarkus.maven;
import static io.quarkus.maven.ExtensionDescriptorMojo.getCodestartArtifact;
-import static org.junit.jupiter.api.Assertions.*;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Collections;
+import java.util.Comparator;
+
+import org.apache.maven.artifact.repository.ArtifactRepository;
+import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy;
+import org.apache.maven.artifact.repository.MavenArtifactRepository;
+import org.apache.maven.artifact.repository.layout.DefaultRepositoryLayout;
+import org.apache.maven.execution.DefaultMavenExecutionRequest;
+import org.apache.maven.execution.MavenExecutionRequest;
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.testing.AbstractMojoTestCase;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.project.ProjectBuilder;
+import org.apache.maven.project.ProjectBuildingException;
+import org.apache.maven.project.ProjectBuildingRequest;
+import org.apache.maven.shared.invoker.DefaultInvocationRequest;
+import org.apache.maven.shared.invoker.DefaultInvoker;
+import org.apache.maven.shared.invoker.InvocationRequest;
+import org.apache.maven.shared.invoker.Invoker;
+import org.apache.maven.shared.invoker.MavenInvocationException;
+import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
+import org.eclipse.aether.DefaultRepositorySystemSession;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-class ExtensionDescriptorMojoTest {
+import io.quarkus.bootstrap.resolver.maven.BootstrapMavenContext;
+import io.quarkus.bootstrap.resolver.maven.MavenArtifactResolver;
+
+class ExtensionDescriptorMojoTest extends AbstractMojoTestCase {
+
+ private static final boolean RESOLVE_OFFLINE = true;
+ // Test resources end up in target/test-classes after filtering
+ public static final String TEST_RESOURCES = "target/test-classes/";
+
+ @BeforeEach
+ public void setup() throws Exception {
+ super.setUp();
+ }
+
+ @AfterEach
+ public void tearDown() throws Exception {
+ super.tearDown();
+
+ // Assume that all our test data has a common org, for ease of cleanup
+ File repoPath = new File(getLocalRepoPath(), "io/quackiverse");
+ deleteDirectory(repoPath);
+ }
@Test
- void testGetCodestartArtifact() {
+ public void shouldExecuteSimplePomCleanly()
+ throws Exception {
+ ExtensionDescriptorMojo mojo = makeMojo("simple-pom-with-checks-disabled");
+ mojo.execute();
+ }
+
+ @Test
+ public void shouldCreateExtensionProperties()
+ throws Exception {
+ ExtensionDescriptorMojo mojo = makeMojo("simple-pom-with-checks-disabled");
+ File propertiesFile = getGeneratedExtensionMetadataFile(mojo.project.getBasedir(),
+ "target/classes/META-INF/quarkus-extension.properties");
+
+ // Tidy up any artifacts from previous runs
+ if (propertiesFile.exists()) {
+ Files.delete(propertiesFile.toPath());
+ }
+ mojo.execute();
+ assertTrue(propertiesFile.exists());
+ }
+
+ @Test
+ public void shouldCreateMetadata()
+ throws Exception {
+ ExtensionDescriptorMojo mojo = makeMojo("simple-pom-with-checks-disabled");
+ File yamlFile = getGeneratedExtensionMetadataFile(mojo.project.getBasedir(),
+ "target/classes/META-INF/quarkus-extension.yaml");
+
+ // Tidy up any artifacts from previous runs
+ if (yamlFile.exists()) {
+ Files.delete(yamlFile.toPath());
+ }
+ mojo.execute();
+ assertTrue(yamlFile.exists());
+
+ String fileContents = readFileAsString(yamlFile);
+ assertYamlContains(fileContents, "name", "an arbitrary name");
+ assertYamlContains(fileContents, "artifact", "io.quackiverse:test-artifact::jar:1.4.2-SNAPSHOT");
+
+ }
+
+ @Test
+ public void shouldProcessRealisticExtensionCleanly()
+ throws Exception {
+
+ // We need to get these poms into our local repository before executing the plugin
+ mavenExecPom("fake-extension-runtime");
+ mavenExecPom("fake-extension-deployment");
+
+ ExtensionDescriptorMojo mojo = makeMojo("fake-extension-runtime");
+ mojo.execute();
+
+ }
+
+ @Test
+ public void shouldFlagMissingDependenciesInARealisticExtension()
+ throws Exception {
+
+ // Build both halves of the extension first so the install phase is done
+ mavenExecPom("fake-extension-runtime-with-missing-dependencies");
+ mavenExecPom("fake-extension-deployment");
+
+ Exception thrown = Assertions.assertThrows(MojoExecutionException.class, () -> {
+ ExtensionDescriptorMojo mojo = makeMojo("fake-extension-runtime-with-missing-dependencies");
+ mojo.execute();
+ });
+ assertTrue("Message format does not match expectations: \n" + thrown.getMessage(),
+ thrown.getMessage().contains(" corresponding runtime artifacts were not found"));
+ assertTrue("Missing artifact 'io.quarkus:quarkus-arc-deployment::jar' is not flagged: \n" + thrown.getMessage(),
+ thrown.getMessage().contains("io.quarkus:quarkus-arc-deployment::jar"));
+ assertTrue(
+ "Missing artifact 'io.quarkus:quarkus-smallrye-context-propagation-deployment::jar' is not flagged: \n"
+ + thrown.getMessage(),
+ thrown.getMessage().contains("io.quarkus:quarkus-smallrye-context-propagation-deployment::jar"));
+ }
+
+ @Test
+ public void shouldFlagIncorrectRuntimeDependencyOnDeployment()
+ throws Exception {
+
+ // Build both halves of the extension first so the install phase is done
+ mavenExecPom("fake-extension-runtime-with-deployment-dependency");
+ mavenExecPom("fake-extension-deployment");
+
+ Exception thrown = Assertions.assertThrows(MojoExecutionException.class, () -> {
+ ExtensionDescriptorMojo mojo = makeMojo("fake-extension-runtime-with-missing-dependencies");
+ mojo.execute();
+ });
+ String message = thrown.getMessage();
+ assertTrue("Message format does not match expectations: \n" + message,
+ message.contains("depends on the following Quarkus extension deployment artifacts")
+ || message.contains("The following deployment artifact(s) appear on the runtime classpath"));
+ }
+
+ // TODO we could also add some more tests about the dependency resolution and other extension descriptor functions here; see ExtensionDescriptorTaskTest for some ideas
+
+ private File getGeneratedExtensionMetadataFile(File project, String child) {
+ return new File(project, child);
+ }
+
+ private void assertYamlContains(File file, String key, String value) throws IOException {
+ assertYamlContains(readFileAsString(file), key, value);
+ }
+
+ // Naive yaml processing; we do it this way for readability, and simplicity, and because it's how a human would check it by eye.
+ private void assertYamlContains(String fileContents, String key, String value) {
+ // ... we should probably cache the file read
+ assertTrue("Missing key '" + key + "' in \n" + fileContents, fileContents.contains(key));
+ assertTrue("Missing value '" + value + "' in \n" + fileContents, fileContents.contains(key + ": \"" + value + "\""));
+
+ }
+
+ private static String readFileAsString(File file) throws IOException {
+ return new String(Files.readAllBytes(file.toPath()));
+ }
+
+ @Test
+ public void testGetCodestartArtifact() {
assertEquals("io.quarkus:my-ext:999-SN",
getCodestartArtifact("io.quarkus:my-ext", "999-SN"));
assertEquals("io.quarkus:my-ext:codestarts:jar:999-SN",
@@ -18,4 +185,96 @@ void testGetCodestartArtifact() {
assertEquals("io.quarkus:my-ext:999-SN",
getCodestartArtifact("io.quarkus:my-ext:${project.version}", "999-SN"));
}
+
+ private ExtensionDescriptorMojo makeMojo(String dirName) throws Exception {
+ File basedir = getTestFile(TEST_RESOURCES + dirName);
+ File pom = new File(basedir, "pom.xml");
+
+ MavenProject project = readMavenProject(basedir);
+ MavenSession session = newMavenSession(project);
+
+ // add localRepo - framework doesn't do this on its own
+ ArtifactRepository localRepo = createLocalArtifactRepository();
+ session.getRequest().setLocalRepository(localRepo);
+
+ final MavenArtifactResolver mvn = new MavenArtifactResolver(
+ new BootstrapMavenContext(BootstrapMavenContext.config()
+ .setCurrentProject(pom.getAbsolutePath())
+ .setOffline(RESOLVE_OFFLINE)));
+
+ ExtensionDescriptorMojo mojo = (ExtensionDescriptorMojo) lookupConfiguredMojo(session,
+ newMojoExecution("extension-descriptor"));
+ mojo.repoSystem = mvn.getSystem();
+ mojo.repoSession = mvn.getSession();
+ return mojo;
+ }
+
+ protected MavenProject readMavenProject(File basedir)
+ throws ProjectBuildingException, ComponentLookupException {
+ File pom = getGeneratedExtensionMetadataFile(basedir, "pom.xml");
+ assertTrue(pom.exists());
+
+ MavenExecutionRequest request = new DefaultMavenExecutionRequest();
+ request.setBaseDirectory(basedir);
+ ProjectBuildingRequest configuration = request.getProjectBuildingRequest();
+ configuration.setRepositorySession(new DefaultRepositorySystemSession());
+ configuration.setLocalRepository(createLocalArtifactRepository());
+ MavenProject project = lookup(ProjectBuilder.class).build(pom, configuration).getProject();
+ assertNotNull(project);
+ return project;
+ }
+
+ private ArtifactRepository createLocalArtifactRepository() {
+ String repo = getLocalRepoPath();
+ File localRepoDir = new File(repo);
+ return new MavenArtifactRepository("local",
+ localRepoDir.toURI().toString(),
+ new DefaultRepositoryLayout(),
+ new ArtifactRepositoryPolicy(true, ArtifactRepositoryPolicy.UPDATE_POLICY_ALWAYS,
+ ArtifactRepositoryPolicy.CHECKSUM_POLICY_IGNORE),
+ new ArtifactRepositoryPolicy(true, ArtifactRepositoryPolicy.UPDATE_POLICY_ALWAYS,
+ ArtifactRepositoryPolicy.CHECKSUM_POLICY_IGNORE)
+
+ );
+ }
+
+ private String getLocalRepoPath() {
+ String repo = System.getProperty("maven.repo.local");
+ if (repo == null) {
+ repo = new File(System.getProperty("user.home"), ".m2/repository").getAbsolutePath();
+ }
+ return repo;
+ }
+
+ private void mavenExecPom(String dir) throws MavenInvocationException {
+ InvocationRequest request = new DefaultInvocationRequest();
+ File projectPath = getTestFile(TEST_RESOURCES, dir + "/pom.xml");
+
+ request.setPomFile(projectPath);
+
+ request.setGoals(Collections.singletonList("install"));
+ Invoker invoker = new DefaultInvoker();
+
+ // This is a bit ugly, but bake in knowledge about where we are in the hierarchy to find maven
+ invoker.setMavenHome(new File("../../").getAbsoluteFile());
+ invoker.setMavenExecutable(new File("../../mvnw").getAbsoluteFile());
+
+ invoker.execute(request);
+ }
+
+ private void deleteDirectory(File fileToDelete) throws IOException {
+
+ if (fileToDelete.exists()) {
+ Path pathToBeDeleted = fileToDelete.toPath();
+
+ Files.walk(pathToBeDeleted)
+ .sorted(Comparator.reverseOrder())
+ .map(Path::toFile)
+ .forEach(File::delete);
+
+ assertFalse("Directory still exists",
+ Files.exists(pathToBeDeleted));
+ }
+ }
+
}
diff --git a/independent-projects/extension-maven-plugin/src/test/resources/fake-extension-deployment/pom.xml b/independent-projects/extension-maven-plugin/src/test/resources/fake-extension-deployment/pom.xml
new file mode 100644
index 00000000000000..baf1a16add1055
--- /dev/null
+++ b/independent-projects/extension-maven-plugin/src/test/resources/fake-extension-deployment/pom.xml
@@ -0,0 +1,102 @@
+
+
+
+
+ 4.0.0
+
+ io.quackiverse
+ quarkus-fake-extension-deployment
+ ${project.version}
+ Quarkus - Fake extension - Deployment
+ A support pom to provide test data to use against the runtime pom
+
+ 1.16
+
+
+
+
+ io.quackiverse
+ quarkus-fake-extension
+ ${project.version}
+
+
+ io.quarkus
+ quarkus-core-deployment
+ 2.12.0.Final
+
+
+ io.quarkus
+ quarkus-arc-deployment
+ 2.12.0.Final
+
+
+ io.quarkus
+ quarkus-datasource-deployment
+ 2.12.0.Final
+
+
+ io.quarkus
+ quarkus-narayana-jta-deployment
+ 2.12.0.Final
+
+
+
+
+ io.quarkus
+ quarkus-smallrye-health
+ true
+ 2.12.0.Final
+
+
+
+ io.agroal
+ agroal-api
+ ${agroal.version}
+
+
+ io.agroal
+ agroal-narayana
+ ${agroal.version}
+
+
+ org.jboss.spec.javax.transaction
+ jboss-transaction-api_1.2_spec
+
+
+
+
+ io.agroal
+ agroal-pool
+ ${agroal.version}
+
+
+ io.quarkus
+ quarkus-credentials
+ 2.12.0.Final
+
+
+
+
+
+
+
+ io.quarkus
+ quarkus-extension-maven-plugin
+ ${project.version}
+
+
+ io.quarkus.agroal
+
+
+
+
+
+
+
+
+
diff --git a/independent-projects/extension-maven-plugin/src/test/resources/fake-extension-runtime-with-deployment-dependency/pom.xml b/independent-projects/extension-maven-plugin/src/test/resources/fake-extension-runtime-with-deployment-dependency/pom.xml
new file mode 100644
index 00000000000000..29b0306e37dc33
--- /dev/null
+++ b/independent-projects/extension-maven-plugin/src/test/resources/fake-extension-runtime-with-deployment-dependency/pom.xml
@@ -0,0 +1,104 @@
+
+
+
+
+ 4.0.0
+
+ io.quackiverse
+ quarkus-fake-extension
+ ${project.version}
+ Quarkus - Fake extension - Runtime
+ An artifact which depends on the deployment artifact
+
+ 1.16
+
+
+
+
+
+ io.quarkus
+ quarkus-core-deployment
+ 2.12.0.Final
+
+
+ io.quarkus
+ quarkus-core
+ 2.12.0.Final
+
+
+ io.quarkus
+ quarkus-arc
+ 2.12.0.Final
+
+
+ io.quarkus
+ quarkus-datasource
+ 2.12.0.Final
+
+
+ io.quarkus
+ quarkus-narayana-jta
+ 2.12.0.Final
+
+
+
+
+ io.quarkus
+ quarkus-smallrye-health
+ true
+ 2.12.0.Final
+
+
+
+ io.agroal
+ agroal-api
+ ${agroal.version}
+
+
+ io.agroal
+ agroal-narayana
+ ${agroal.version}
+
+
+ org.jboss.spec.javax.transaction
+ jboss-transaction-api_1.2_spec
+
+
+
+
+ io.agroal
+ agroal-pool
+ ${agroal.version}
+
+
+ io.quarkus
+ quarkus-credentials
+ 2.12.0.Final
+
+
+
+
+
+
+
+ io.quarkus
+ quarkus-extension-maven-plugin
+ ${project.version}
+
+
+ io.quarkus.agroal
+
+
+
+
+
+
+
+
+
diff --git a/independent-projects/extension-maven-plugin/src/test/resources/fake-extension-runtime-with-missing-dependencies/pom.xml b/independent-projects/extension-maven-plugin/src/test/resources/fake-extension-runtime-with-missing-dependencies/pom.xml
new file mode 100644
index 00000000000000..ab711fcd9413b3
--- /dev/null
+++ b/independent-projects/extension-maven-plugin/src/test/resources/fake-extension-runtime-with-missing-dependencies/pom.xml
@@ -0,0 +1,43 @@
+
+
+
+
+ 4.0.0
+
+ io.quackiverse
+ quarkus-fake-extension
+ ${project.version}
+ Quarkus - Fake extension - Deployment
+ A version of the fake extension whose pom is missing dependencies
+
+
+ io.quarkus
+ quarkus-core
+ ${project.version}
+
+
+
+
+
+
+
+
+ io.quarkus
+ quarkus-extension-maven-plugin
+ ${project.version}
+
+
+ io.quarkus.agroal
+
+
+
+
+
+
+
+
+
diff --git a/independent-projects/extension-maven-plugin/src/test/resources/fake-extension-runtime/pom.xml b/independent-projects/extension-maven-plugin/src/test/resources/fake-extension-runtime/pom.xml
new file mode 100644
index 00000000000000..970d613ddb987f
--- /dev/null
+++ b/independent-projects/extension-maven-plugin/src/test/resources/fake-extension-runtime/pom.xml
@@ -0,0 +1,99 @@
+
+
+
+
+ 4.0.0
+
+ io.quackiverse
+ quarkus-fake-extension
+ ${project.version}
+ Quarkus - Fake extension - Runtime
+ Pool JDBC database connections (included in Hibernate ORM)
+
+ 1.16
+
+
+
+
+
+ io.quarkus
+ quarkus-core
+ 2.12.0.Final
+
+
+ io.quarkus
+ quarkus-arc
+ 2.12.0.Final
+
+
+ io.quarkus
+ quarkus-datasource
+ 2.12.0.Final
+
+
+ io.quarkus
+ quarkus-narayana-jta
+ 2.12.0.Final
+
+
+
+
+ io.quarkus
+ quarkus-smallrye-health
+ true
+ 2.12.0.Final
+
+
+
+ io.agroal
+ agroal-api
+ ${agroal.version}
+
+
+ io.agroal
+ agroal-narayana
+ ${agroal.version}
+
+
+ org.jboss.spec.javax.transaction
+ jboss-transaction-api_1.2_spec
+
+
+
+
+ io.agroal
+ agroal-pool
+ ${agroal.version}
+
+
+ io.quarkus
+ quarkus-credentials
+ 2.12.0.Final
+
+
+
+
+
+
+
+ io.quarkus
+ quarkus-extension-maven-plugin
+ ${project.version}
+
+
+ io.quarkus.agroal
+
+
+
+
+
+
+
+
+
diff --git a/independent-projects/extension-maven-plugin/src/test/resources/simple-pom-with-checks-disabled/pom.xml b/independent-projects/extension-maven-plugin/src/test/resources/simple-pom-with-checks-disabled/pom.xml
new file mode 100644
index 00000000000000..387378d514dbd4
--- /dev/null
+++ b/independent-projects/extension-maven-plugin/src/test/resources/simple-pom-with-checks-disabled/pom.xml
@@ -0,0 +1,23 @@
+
+ 4.0.0
+ io.quackiverse
+ test-artifact
+ 1.4.2-SNAPSHOT
+ an arbitrary name
+
+
+ true
+ true
+
+
+
+
+
+ quarkus.io
+ extension-maven-plugin
+ ${project.version}
+
+
+
+