diff --git a/.github/workflows/maven-verify.yml b/.github/workflows/maven-verify.yml index fa7dc4b..9177951 100644 --- a/.github/workflows/maven-verify.yml +++ b/.github/workflows/maven-verify.yml @@ -26,4 +26,4 @@ jobs: name: Verify uses: apache/maven-gh-actions-shared/.github/workflows/maven-verify.yml@v4 with: - jdk-matrix: '[ "8", "11", "17" ]' + jdk-matrix: '[ "17", "21" ]' diff --git a/maven-plugin-testing-harness/pom.xml b/maven-plugin-testing-harness/pom.xml index beaa39d..a1d4e4d 100644 --- a/maven-plugin-testing-harness/pom.xml +++ b/maven-plugin-testing-harness/pom.xml @@ -35,7 +35,7 @@ under the License. org.junit junit-bom - 5.10.0 + 5.10.1 pom import @@ -62,18 +62,33 @@ under the License. ${mavenVersion} provided + + org.apache.maven + maven-model-builder + ${mavenVersion} + provided + org.apache.maven maven-plugin-api ${mavenVersion} provided + + org.apache.maven + maven-xml-impl + ${mavenVersion} + org.apache.maven maven-resolver-provider ${mavenVersion} provided + + org.eclipse.sisu + org.eclipse.sisu.plexus + @@ -85,7 +100,7 @@ under the License. org.codehaus.plexus plexus-xml - 4.0.0 + 4.0.3 true @@ -97,14 +112,17 @@ under the License. org.codehaus.plexus plexus-testing - 1.1.0 - - - - org.junit.jupiter - junit-jupiter-api - - + 1.3.0 + + + com.google.inject + guice + 6.0.0 + + + org.ow2.asm + asm + 9.6 @@ -112,12 +130,6 @@ under the License. guava 32.0.1-jre - - junit - junit - 4.13.2 - true - org.junit.jupiter junit-jupiter-api @@ -131,7 +143,7 @@ under the License. org.slf4j slf4j-simple - 1.7.36 + 2.0.13 test diff --git a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/di/testing/MavenDIExtension.java b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/di/testing/MavenDIExtension.java new file mode 100644 index 0000000..c6f3b86 --- /dev/null +++ b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/di/testing/MavenDIExtension.java @@ -0,0 +1,150 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.maven.api.di.testing; + +import java.io.*; + +import org.apache.maven.di.Injector; +import org.apache.maven.di.Key; +import org.apache.maven.di.impl.DIException; +import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; + +/** + * This is a slightly modified version of the original plexus class + * available at https://raw.githubusercontent.com/codehaus-plexus/plexus-containers/master/plexus-container-default/ + * src/main/java/org/codehaus/plexus/PlexusTestCase.java + * in order to migrate the tests to JUnit 4. + * + * @author Jason van Zyl + * @author Trygve Laugstøl + * @author Michal Maczka + * @author Guillaume Nodet + */ +public class MavenDIExtension implements BeforeEachCallback, AfterEachCallback { + protected static ExtensionContext context; + protected Injector injector; + protected static String basedir; + + @Override + public void beforeEach(ExtensionContext context) throws Exception { + basedir = getBasedir(); + + setContext(context); + + getInjector().bindInstance((Class) context.getRequiredTestClass(), context.getRequiredTestInstance()); + getInjector().injectInstance(context.getRequiredTestInstance()); + } + + protected void setContext(ExtensionContext context) { + this.context = context; + } + + @SuppressWarnings("unchecked") + protected void setupContainer() { + try { + injector = Injector.create(); + injector.bindInstance(ExtensionContext.class, this.context); + injector.discover(this.context.getRequiredTestClass().getClassLoader()); + injector.bindInstance(Injector.class, injector); + injector.bindInstance((Class) this.context.getRequiredTestClass(), this.context.getRequiredTestInstance()); + } catch (Exception e) { + throw new IllegalArgumentException("Failed to create DI injector.", e); + } + } + + @Override + public void afterEach(ExtensionContext context) throws Exception { + if (injector != null) { + // TODO: implement + // injector.dispose(); + injector = null; + } + } + + public Injector getInjector() { + if (injector == null) { + setupContainer(); + } + + return injector; + } + + // ---------------------------------------------------------------------- + // Container access + // ---------------------------------------------------------------------- + + protected T lookup(Class componentClass) throws DIException { + return getInjector().getInstance(componentClass); + } + + protected T lookup(Class componentClass, String roleHint) throws DIException { + return getInjector().getInstance(Key.ofType(componentClass, roleHint)); + } + + protected T lookup(Class componentClass, Object qualifier) throws DIException { + return getInjector().getInstance(Key.ofType(componentClass, qualifier)); + } + + protected void release(Object component) throws DIException { + // TODO: implement + // getInjector().release(component); + } + + // ---------------------------------------------------------------------- + // Helper methods for sub classes + // ---------------------------------------------------------------------- + + public static File getTestFile(String path) { + return new File(getBasedir(), path); + } + + public static File getTestFile(String basedir, String path) { + File basedirFile = new File(basedir); + + if (!basedirFile.isAbsolute()) { + basedirFile = getTestFile(basedir); + } + + return new File(basedirFile, path); + } + + public static String getTestPath(String path) { + return getTestFile(path).getAbsolutePath(); + } + + public static String getTestPath(String basedir, String path) { + return getTestFile(basedir, path).getAbsolutePath(); + } + + public static String getBasedir() { + if (basedir != null) { + return basedir; + } + + basedir = System.getProperty("basedir"); + + if (basedir == null) { + basedir = new File("").getAbsolutePath(); + } + + return basedir; + } +} diff --git a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/di/testing/MavenDITest.java b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/di/testing/MavenDITest.java new file mode 100644 index 0000000..ddc6d2f --- /dev/null +++ b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/di/testing/MavenDITest.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.maven.api.di.testing; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.junit.jupiter.api.extension.ExtendWith; + +/** + * Plexus test + */ +@Retention(RetentionPolicy.RUNTIME) +@ExtendWith(MavenDIExtension.class) +@Target(ElementType.TYPE) +public @interface MavenDITest {} diff --git a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/Basedir.java b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/Basedir.java new file mode 100644 index 0000000..69eaba2 --- /dev/null +++ b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/Basedir.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.maven.api.plugin.testing; + +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Mojo parameters container + */ +@Retention(RetentionPolicy.RUNTIME) +@Inherited +public @interface Basedir { + String value() default ""; +} diff --git a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/InjectMojo.java b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/InjectMojo.java index e094d06..272eb9b 100644 --- a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/InjectMojo.java +++ b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/InjectMojo.java @@ -18,6 +18,7 @@ */ package org.apache.maven.api.plugin.testing; +import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -25,11 +26,10 @@ * */ @Retention(RetentionPolicy.RUNTIME) +@Inherited public @interface InjectMojo { String goal(); - String pom(); - - boolean empty() default false; + String pom() default ""; } diff --git a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/MojoExtension.java b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/MojoExtension.java index b217fab..4bedb05 100644 --- a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/MojoExtension.java +++ b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/MojoExtension.java @@ -18,66 +18,71 @@ */ package org.apache.maven.api.plugin.testing; -import java.io.BufferedReader; -import java.io.File; -import java.io.InputStream; -import java.io.Reader; -import java.io.StringReader; +import java.io.*; import java.lang.reflect.AccessibleObject; +import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Field; import java.net.URL; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; -import com.google.inject.internal.ProviderMethodsModule; import org.apache.maven.api.MojoExecution; import org.apache.maven.api.Project; import org.apache.maven.api.Session; +import org.apache.maven.api.di.Named; +import org.apache.maven.api.di.Priority; +import org.apache.maven.api.di.Provides; +import org.apache.maven.api.di.Singleton; +import org.apache.maven.api.di.testing.MavenDIExtension; +import org.apache.maven.api.model.Build; +import org.apache.maven.api.model.ConfigurationContainer; +import org.apache.maven.api.model.Model; import org.apache.maven.api.plugin.Log; import org.apache.maven.api.plugin.Mojo; +import org.apache.maven.api.plugin.testing.stubs.*; +import org.apache.maven.api.services.ArtifactDeployer; +import org.apache.maven.api.services.ArtifactFactory; +import org.apache.maven.api.services.ArtifactInstaller; +import org.apache.maven.api.services.ArtifactManager; +import org.apache.maven.api.services.LocalRepositoryManager; +import org.apache.maven.api.services.ProjectBuilder; +import org.apache.maven.api.services.ProjectManager; +import org.apache.maven.api.services.RepositoryFactory; +import org.apache.maven.api.services.VersionParser; +import org.apache.maven.api.services.xml.ModelXmlFactory; import org.apache.maven.api.xml.XmlNode; import org.apache.maven.configuration.internal.EnhancedComponentConfigurator; +import org.apache.maven.di.Injector; +import org.apache.maven.di.Key; +import org.apache.maven.di.impl.DIException; import org.apache.maven.internal.impl.DefaultLog; +import org.apache.maven.internal.impl.InternalSession; import org.apache.maven.internal.xml.XmlNodeImpl; +import org.apache.maven.internal.xml.XmlPlexusConfiguration; import org.apache.maven.lifecycle.internal.MojoDescriptorCreator; +import org.apache.maven.model.path.DefaultModelPathTranslator; +import org.apache.maven.model.path.DefaultPathTranslator; +import org.apache.maven.model.v4.MavenMerger; +import org.apache.maven.model.v4.MavenStaxReader; import org.apache.maven.plugin.PluginParameterExpressionEvaluatorV4; import org.apache.maven.plugin.descriptor.MojoDescriptor; import org.apache.maven.plugin.descriptor.Parameter; import org.apache.maven.plugin.descriptor.PluginDescriptor; import org.apache.maven.plugin.descriptor.PluginDescriptorBuilder; -import org.codehaus.plexus.DefaultPlexusContainer; -import org.codehaus.plexus.PlexusContainer; -import org.codehaus.plexus.component.configurator.ComponentConfigurator; import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException; import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator; import org.codehaus.plexus.component.configurator.expression.TypeAwareExpressionEvaluator; -import org.codehaus.plexus.component.repository.ComponentDescriptor; -import org.codehaus.plexus.component.repository.exception.ComponentLookupException; -import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration; -import org.codehaus.plexus.testing.PlexusExtension; -import org.codehaus.plexus.util.InterpolationFilterReader; -import org.codehaus.plexus.util.ReaderFactory; import org.codehaus.plexus.util.ReflectionUtils; import org.codehaus.plexus.util.xml.XmlStreamReader; import org.codehaus.plexus.util.xml.Xpp3Dom; import org.codehaus.plexus.util.xml.Xpp3DomBuilder; -import org.junit.jupiter.api.extension.ExtensionContext; -import org.junit.jupiter.api.extension.ParameterContext; -import org.junit.jupiter.api.extension.ParameterResolutionException; -import org.junit.jupiter.api.extension.ParameterResolver; +import org.eclipse.aether.RepositorySystem; +import org.junit.jupiter.api.extension.*; +import org.junit.platform.commons.support.AnnotationSupport; import org.slf4j.LoggerFactory; /** @@ -87,8 +92,25 @@ * @see MojoTest * @see InjectMojo * @see MojoParameter + * @see Basedir */ -public class MojoExtension extends PlexusExtension implements ParameterResolver { +public class MojoExtension extends MavenDIExtension implements ParameterResolver, BeforeEachCallback { + + protected static String pluginBasedir; + protected static String basedir; + + public static String getTestId() { + return context.getRequiredTestClass().getSimpleName() + "-" + + context.getRequiredTestMethod().getName(); + } + + public static String getBasedir() { + return Objects.requireNonNull(basedir != null ? basedir : MavenDIExtension.basedir); + } + + public static String getPluginBasedir() { + return Objects.requireNonNull(pluginBasedir); + } @Override public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) @@ -101,115 +123,381 @@ public boolean supportsParameter(ParameterContext parameterContext, ExtensionCon public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException { try { - InjectMojo injectMojo = parameterContext - .findAnnotation(InjectMojo.class) - .orElseGet(() -> parameterContext.getDeclaringExecutable().getAnnotation(InjectMojo.class)); - - Set mojoParameters = - new HashSet<>(parameterContext.findRepeatableAnnotations(MojoParameter.class)); - - Optional.ofNullable(parameterContext.getDeclaringExecutable().getAnnotation(MojoParameter.class)) - .ifPresent(mojoParameters::add); - - Optional.ofNullable(parameterContext.getDeclaringExecutable().getAnnotation(MojoParameters.class)) - .map(MojoParameters::value) - .map(Arrays::asList) - .ifPresent(mojoParameters::addAll); - Class holder = parameterContext.getTarget().get().getClass(); PluginDescriptor descriptor = extensionContext .getStore(ExtensionContext.Namespace.GLOBAL) .get(PluginDescriptor.class, PluginDescriptor.class); - return lookupMojo(holder, injectMojo, mojoParameters, descriptor); + Model model = + extensionContext.getStore(ExtensionContext.Namespace.GLOBAL).get(Model.class, Model.class); + InjectMojo parameterInjectMojo = + parameterContext.getAnnotatedElement().getAnnotation(InjectMojo.class); + String goal; + if (parameterInjectMojo != null) { + String pom = parameterInjectMojo.pom(); + if (pom != null && !pom.isEmpty()) { + try (Reader r = openPomUrl(holder, pom, new Path[1])) { + Model localModel = new MavenStaxReader().read(r); + model = new MavenMerger().merge(localModel, model, false, null); + org.apache.maven.model.Model modelV3 = new org.apache.maven.model.Model(model); + new DefaultModelPathTranslator(new DefaultPathTranslator()) + .alignToBaseDirectory(modelV3, new File(getBasedir()), null); + model = modelV3.getDelegate(); + } + } + goal = parameterInjectMojo.goal(); + } else { + InjectMojo methodInjectMojo = AnnotationSupport.findAnnotation( + parameterContext.getDeclaringExecutable(), InjectMojo.class) + .orElse(null); + if (methodInjectMojo != null) { + goal = methodInjectMojo.goal(); + } else { + goal = getGoalFromMojoImplementationClass( + parameterContext.getParameter().getType()); + } + } + + Set mojoParameters = new LinkedHashSet<>(); + for (AnnotatedElement ae : + Arrays.asList(parameterContext.getDeclaringExecutable(), parameterContext.getAnnotatedElement())) { + mojoParameters.addAll(AnnotationSupport.findRepeatableAnnotations(ae, MojoParameter.class)); + } + String[] coord = mojoCoordinates(goal); + + XmlNode pluginConfiguration = model.getBuild().getPlugins().stream() + .filter(p -> + Objects.equals(p.getGroupId(), coord[0]) && Objects.equals(p.getArtifactId(), coord[1])) + .map(ConfigurationContainer::getConfiguration) + .findFirst() + .orElseGet(() -> new XmlNodeImpl("config")); + List children = mojoParameters.stream() + .map(mp -> new XmlNodeImpl(mp.name(), mp.value())) + .collect(Collectors.toList()); + XmlNode config = new XmlNodeImpl("configuration", null, null, children, null); + pluginConfiguration = XmlNode.merge(config, pluginConfiguration); + + // load default config + // pluginkey = groupId : artifactId : version : goal + Mojo mojo = lookup(Mojo.class, coord[0] + ":" + coord[1] + ":" + coord[2] + ":" + coord[3]); + for (MojoDescriptor mojoDescriptor : descriptor.getMojos()) { + if (Objects.equals(mojoDescriptor.getGoal(), coord[3])) { + if (pluginConfiguration != null) { + pluginConfiguration = finalizeConfig(pluginConfiguration, mojoDescriptor); + } + } + } + + Session session = getInjector().getInstance(Session.class); + Project project = getInjector().getInstance(Project.class); + MojoExecution mojoExecution = getInjector().getInstance(MojoExecution.class); + ExpressionEvaluator evaluator = new WrapEvaluator( + getInjector(), new PluginParameterExpressionEvaluatorV4(session, project, mojoExecution)); + + EnhancedComponentConfigurator configurator = new EnhancedComponentConfigurator(); + configurator.configureComponent(mojo, new XmlPlexusConfiguration(pluginConfiguration), evaluator, null); + return mojo; } catch (Exception e) { - throw new ParameterResolutionException("Unable to resolve parameter", e); + throw new ParameterResolutionException("Unable to resolve mojo", e); } } + /** + * The @Mojo annotation is only retained in the class file, not at runtime, + * so we need to actually read the class file with ASM to find the annotation and + * the goal. + */ + private static String getGoalFromMojoImplementationClass(Class cl) throws IOException { + return cl.getAnnotation(Named.class).value(); + } + @Override public void beforeEach(ExtensionContext context) throws Exception { - // TODO provide protected setters in PlexusExtension - Field field = PlexusExtension.class.getDeclaredField("basedir"); - field.setAccessible(true); - field.set(null, getBasedir()); - field = PlexusExtension.class.getDeclaredField("context"); - field.setAccessible(true); - field.set(this, context); - - getContainer().addComponent(getContainer(), PlexusContainer.class.getName()); + if (pluginBasedir == null) { + pluginBasedir = MavenDIExtension.getBasedir(); + } + basedir = AnnotationSupport.findAnnotation(context.getElement().get(), Basedir.class) + .map(Basedir::value) + .orElse(pluginBasedir); + if (basedir != null) { + if (basedir.isEmpty()) { + basedir = pluginBasedir + "/target/tests/" + + context.getRequiredTestClass().getSimpleName() + "/" + + context.getRequiredTestMethod().getName(); + } else { + basedir = basedir.replace("${basedir}", pluginBasedir); + } + } - ((DefaultPlexusContainer) getContainer()).addPlexusInjector(Collections.emptyList(), binder -> { - binder.install(ProviderMethodsModule.forObject(context.getRequiredTestInstance())); - binder.requestInjection(context.getRequiredTestInstance()); - binder.bind(Log.class).toInstance(new DefaultLog(LoggerFactory.getLogger("anonymous"))); + setContext(context); + + /* + binder.install(ProviderMethodsModule.forObject(context.getRequiredTestInstance())); + binder.requestInjection(context.getRequiredTestInstance()); + binder.bind(Log.class).toInstance(new DefaultLog(LoggerFactory.getLogger("anonymous"))); + binder.bind(ExtensionContext.class).toInstance(context); + // Load maven 4 api Services interfaces and try to bind them to the (possible) mock instances + // returned by the (possibly) mock InternalSession + try { + for (ClassPath.ClassInfo clazz : + ClassPath.from(getClassLoader()).getAllClasses()) { + if ("org.apache.maven.api.services".equals(clazz.getPackageName())) { + Class load = clazz.load(); + if (Service.class.isAssignableFrom(load)) { + Class svc = (Class) load; + binder.bind(svc).toProvider(() -> { + try { + return getContainer() + .lookup(InternalSession.class) + .getService(svc); + } catch (ComponentLookupException e) { + throw new RuntimeException("Unable to lookup service " + svc.getName()); + } + }); + } + } + } + } catch (Exception e) { + throw new RuntimeException("Unable to bind session services", e); + } + + */ + + Path basedirPath = Paths.get(getBasedir()); + + InjectMojo mojo = AnnotationSupport.findAnnotation(context.getElement().get(), InjectMojo.class) + .orElse(null); + Model defaultModel = Model.newBuilder() + .groupId("myGroupId") + .artifactId("myArtifactId") + .version("1.0-SNAPSHOT") + .packaging("jar") + .build(Build.newBuilder() + .directory(basedirPath.resolve("target").toString()) + .outputDirectory(basedirPath.resolve("target/classes").toString()) + .sourceDirectory(basedirPath.resolve("src/main/java").toString()) + .testSourceDirectory( + basedirPath.resolve("src/test/java").toString()) + .testOutputDirectory( + basedirPath.resolve("target/test-classes").toString()) + .build()) + .build(); + Path[] modelPath = new Path[] {null}; + Model tmodel = null; + if (mojo != null) { + String pom = mojo.pom(); + if (pom != null && !pom.isEmpty()) { + try (Reader r = openPomUrl(context.getRequiredTestClass(), pom, modelPath)) { + tmodel = new MavenStaxReader().read(r); + } + } else { + Path pomPath = basedirPath.resolve("pom.xml"); + if (Files.exists(pomPath)) { + try (Reader r = Files.newBufferedReader(pomPath)) { + tmodel = new MavenStaxReader().read(r); + modelPath[0] = pomPath; + } + } + } + } + Model model; + if (tmodel == null) { + model = defaultModel; + } else { + model = new MavenMerger().merge(tmodel, defaultModel, false, null); + } + org.apache.maven.model.Model modelV3 = new org.apache.maven.model.Model(tmodel); + new DefaultModelPathTranslator(new DefaultPathTranslator()) + .alignToBaseDirectory(modelV3, new File(getBasedir()), null); + tmodel = modelV3.getDelegate(); + context.getStore(ExtensionContext.Namespace.GLOBAL).put(Model.class, tmodel); + + // mojo execution + // Map map = getInjector().getContext().getContextData(); + ClassLoader classLoader = context.getRequiredTestClass().getClassLoader(); + PluginDescriptor pluginDescriptor = new PluginDescriptorBuilder().build(() -> { + InputStream is = Objects.requireNonNull( + classLoader.getResourceAsStream(getPluginDescriptorLocation()), + "Unable to find plugin descriptor: " + getPluginDescriptorLocation()); + Reader reader = new BufferedReader(new XmlStreamReader(is)); + return reader; + // return new InterpolationFilterReader(reader, map, "${", "}"); }); + context.getStore(ExtensionContext.Namespace.GLOBAL).put(PluginDescriptor.class, pluginDescriptor); + // for (ComponentDescriptor desc : pluginDescriptor.getComponents()) { + // getContainer().addComponentDescriptor(desc); + // } + + @SuppressWarnings({"unused", "MagicNumber"}) + class Foo { + + @Provides + @Singleton + @Priority(-10) + private InternalSession createSession() { + return SessionStub.getMockSession(getBasedir()); + } - Map map = getContainer().getContext().getContextData(); + @Provides + @Singleton + @Priority(-10) + private Project createProject(InternalSession s) { + ProjectStub stub = new ProjectStub(); + if (!"pom".equals(model.getPackaging())) { + ArtifactStub artifact = new ArtifactStub( + model.getGroupId(), model.getArtifactId(), "", model.getVersion(), model.getPackaging()); + stub.setMainArtifact(artifact); + } + stub.setModel(model); + stub.setBasedir(Paths.get(MojoExtension.getBasedir())); + stub.setPomPath(modelPath[0]); + s.getService(ArtifactManager.class).setPath(stub.getPomArtifact(), modelPath[0]); + return stub; + } - ClassLoader classLoader = context.getRequiredTestClass().getClassLoader(); - try (InputStream is = Objects.requireNonNull( - classLoader.getResourceAsStream(getPluginDescriptorLocation()), - "Unable to find plugin descriptor: " + getPluginDescriptorLocation()); - Reader reader = new BufferedReader(new XmlStreamReader(is)); - InterpolationFilterReader interpolationReader = new InterpolationFilterReader(reader, map, "${", "}")) { + @Provides + @Singleton + @Priority(-10) + private MojoExecution createMojoExecution() { + MojoExecutionStub mes = new MojoExecutionStub("executionId", null); + if (mojo != null) { + String goal = mojo.goal(); + int idx = goal.lastIndexOf(':'); + if (idx >= 0) { + goal = goal.substring(idx + 1); + } + mes.setGoal(goal); + MojoDescriptor mojoDescriptor = pluginDescriptor.getMojo(goal); + mes.setDescriptor(mojoDescriptor != null ? mojoDescriptor.getMojoDescriptorV4() : null); + } + PluginStub plugin = new PluginStub(); + plugin.setDescriptor(pluginDescriptor.getPluginDescriptorV4()); + mes.setPlugin(plugin); + return mes; + } - PluginDescriptor pluginDescriptor = new PluginDescriptorBuilder().build(interpolationReader); + @Provides + @Singleton + @Priority(-10) + private Log createLog() { + return new DefaultLog(LoggerFactory.getLogger("anonymous")); + } - context.getStore(ExtensionContext.Namespace.GLOBAL).put(PluginDescriptor.class, pluginDescriptor); + @Provides + static RepositorySystemSupplier newRepositorySystemSupplier() { + return new RepositorySystemSupplier(); + } - for (ComponentDescriptor desc : pluginDescriptor.getComponents()) { - getContainer().addComponentDescriptor(desc); + @Provides + static RepositorySystem newRepositorySystem(RepositorySystemSupplier repositorySystemSupplier) { + return repositorySystemSupplier.getRepositorySystem(); + } + + @Provides + @Priority(10) + static RepositoryFactory newRepositoryFactory(Session session) { + return session.getService(RepositoryFactory.class); + } + + @Provides + @Priority(10) + static VersionParser newVersionParser(Session session) { + return session.getService(VersionParser.class); + } + + @Provides + @Priority(10) + static LocalRepositoryManager newLocalRepositoryManager(Session session) { + return session.getService(LocalRepositoryManager.class); + } + + @Provides + @Priority(10) + static ArtifactInstaller newArtifactInstaller(Session session) { + return session.getService(ArtifactInstaller.class); + } + + @Provides + @Priority(10) + static ArtifactDeployer newArtifactDeployer(Session session) { + return session.getService(ArtifactDeployer.class); + } + + @Provides + @Priority(10) + static ArtifactManager newArtifactManager(Session session) { + return session.getService(ArtifactManager.class); + } + + @Provides + @Priority(10) + static ProjectManager newProjectManager(Session session) { + return session.getService(ProjectManager.class); + } + + @Provides + @Priority(10) + static ArtifactFactory newArtifactFactory(Session session) { + return session.getService(ArtifactFactory.class); + } + + @Provides + @Priority(10) + static ProjectBuilder newProjectBuilder(Session session) { + return session.getService(ProjectBuilder.class); + } + + @Provides + @Priority(10) + static ModelXmlFactory newModelXmlFactory(Session session) { + return session.getService(ModelXmlFactory.class); } } - } - protected String getPluginDescriptorLocation() { - return "META-INF/maven/plugin.xml"; + getInjector().bindInstance(Foo.class, new Foo()); + + getInjector().injectInstance(context.getRequiredTestInstance()); + + // SessionScope sessionScope = getInjector().getInstance(SessionScope.class); + // sessionScope.enter(); + // sessionScope.seed(Session.class, s); + // sessionScope.seed(InternalSession.class, s); + + // MojoExecutionScope mojoExecutionScope = getInjector().getInstance(MojoExecutionScope.class); + // mojoExecutionScope.enter(); + // mojoExecutionScope.seed(Project.class, p); + // mojoExecutionScope.seed(MojoExecution.class, me); } - private Mojo lookupMojo( - Class holder, - InjectMojo injectMojo, - Collection mojoParameters, - PluginDescriptor descriptor) - throws Exception { - String goal = injectMojo.goal(); - String pom = injectMojo.pom(); - String[] coord = mojoCoordinates(goal); - Xpp3Dom pomDom; + private Reader openPomUrl(Class holder, String pom, Path[] modelPath) throws IOException { if (pom.startsWith("file:")) { Path path = Paths.get(getBasedir()).resolve(pom.substring("file:".length())); - pomDom = Xpp3DomBuilder.build(ReaderFactory.newXmlReader(path.toFile())); + modelPath[0] = path; + return Files.newBufferedReader(path); } else if (pom.startsWith("classpath:")) { URL url = holder.getResource(pom.substring("classpath:".length())); if (url == null) { throw new IllegalStateException("Unable to find pom on classpath: " + pom); } - pomDom = Xpp3DomBuilder.build(ReaderFactory.newXmlReader(url.openStream())); + return new XmlStreamReader(url.openStream()); } else if (pom.contains("")) { - pomDom = Xpp3DomBuilder.build(new StringReader(pom)); + return new StringReader(pom); } else { Path path = Paths.get(getBasedir()).resolve(pom); - pomDom = Xpp3DomBuilder.build(ReaderFactory.newXmlReader(path.toFile())); - } - XmlNode pluginConfiguration = extractPluginConfiguration(coord[1], pomDom); - if (!mojoParameters.isEmpty()) { - List children = mojoParameters.stream() - .map(mp -> new XmlNodeImpl(mp.name(), mp.value())) - .collect(Collectors.toList()); - XmlNode config = new XmlNodeImpl("configuration", null, null, children, null); - pluginConfiguration = XmlNode.merge(config, pluginConfiguration); + modelPath[0] = path; + return Files.newBufferedReader(path); } - Mojo mojo = lookupMojo(coord, pluginConfiguration, descriptor); - return mojo; + } + + protected String getPluginDescriptorLocation() { + return "META-INF/maven/plugin.xml"; } protected String[] mojoCoordinates(String goal) throws Exception { if (goal.matches(".*:.*:.*:.*")) { return goal.split(":"); } else { - Path pluginPom = Paths.get(getBasedir(), "pom.xml"); - Xpp3Dom pluginPomDom = Xpp3DomBuilder.build(ReaderFactory.newXmlReader(pluginPom.toFile())); + Path pluginPom = Paths.get(getPluginBasedir(), "pom.xml"); + Xpp3Dom pluginPomDom = Xpp3DomBuilder.build(Files.newBufferedReader(pluginPom)); String artifactId = pluginPomDom.getChild("artifactId").getValue(); String groupId = resolveFromRootThenParent(pluginPomDom, "groupId"); String version = resolveFromRootThenParent(pluginPomDom, "version"); @@ -217,55 +505,16 @@ protected String[] mojoCoordinates(String goal) throws Exception { } } - /** - * lookup the mojo while we have all the relevent information - */ - protected Mojo lookupMojo(String[] coord, XmlNode pluginConfiguration, PluginDescriptor descriptor) - throws Exception { - // pluginkey = groupId : artifactId : version : goal - Mojo mojo = lookup(Mojo.class, coord[0] + ":" + coord[1] + ":" + coord[2] + ":" + coord[3]); - for (MojoDescriptor mojoDescriptor : descriptor.getMojos()) { - if (Objects.equals( - mojoDescriptor.getImplementation(), mojo.getClass().getName())) { - if (pluginConfiguration != null) { - pluginConfiguration = finalizeConfig(pluginConfiguration, mojoDescriptor); - } - } - } - if (pluginConfiguration != null) { - Session session = getContainer().lookup(Session.class); - Project project; - try { - project = getContainer().lookup(Project.class); - } catch (ComponentLookupException e) { - project = null; - } - org.apache.maven.plugin.MojoExecution mojoExecution; - try { - MojoExecution me = getContainer().lookup(MojoExecution.class); - mojoExecution = new org.apache.maven.plugin.MojoExecution( - new org.apache.maven.model.Plugin(me.getPlugin()), me.getGoal(), me.getExecutionId()); - } catch (ComponentLookupException e) { - mojoExecution = null; - } - ExpressionEvaluator evaluator = new WrapEvaluator( - getContainer(), new PluginParameterExpressionEvaluatorV4(session, project, mojoExecution)); - ComponentConfigurator configurator = new EnhancedComponentConfigurator(); - configurator.configureComponent( - mojo, - new XmlPlexusConfiguration(new Xpp3Dom(pluginConfiguration)), - evaluator, - getContainer().getContainerRealm()); - } - - return mojo; - } - private XmlNode finalizeConfig(XmlNode config, MojoDescriptor mojoDescriptor) { List children = new ArrayList<>(); if (mojoDescriptor != null && mojoDescriptor.getParameters() != null) { - XmlNode defaultConfiguration = - MojoDescriptorCreator.convert(mojoDescriptor).getDom(); + XmlNode defaultConfiguration; + if (mojoDescriptor.isV4Api()) { + defaultConfiguration = MojoDescriptorCreator.convert(mojoDescriptor.getMojoDescriptorV4()); + } else { + defaultConfiguration = + MojoDescriptorCreator.convert(mojoDescriptor).getDom(); + } for (Parameter parameter : mojoDescriptor.getParameters()) { XmlNode parameterConfiguration = config.getChild(parameter.getName()); if (parameterConfiguration == null) { @@ -387,11 +636,11 @@ public static void setVariableValueToObject(Object object, String variable, Obje static class WrapEvaluator implements TypeAwareExpressionEvaluator { - private final PlexusContainer container; + private final Injector injector; private final TypeAwareExpressionEvaluator evaluator; - WrapEvaluator(PlexusContainer container, TypeAwareExpressionEvaluator evaluator) { - this.container = container; + WrapEvaluator(Injector injector, TypeAwareExpressionEvaluator evaluator) { + this.injector = injector; this.evaluator = evaluator; } @@ -407,8 +656,8 @@ public Object evaluate(String expression, Class type) throws ExpressionEvalua String expr = stripTokens(expression); if (expr != null) { try { - value = container.lookup(type, expr); - } catch (ComponentLookupException e) { + value = injector.getInstance(Key.of(type, expr)); + } catch (DIException e) { // nothing } } @@ -428,4 +677,35 @@ public File alignToBaseDirectory(File path) { return evaluator.alignToBaseDirectory(path); } } + + /* + private Scope getScopeInstanceOrNull(final Injector injector, final Binding binding) { + return binding.acceptScopingVisitor(new DefaultBindingScopingVisitor() { + + @Override + public Scope visitScopeAnnotation(Class scopeAnnotation) { + throw new RuntimeException(String.format( + "I don't know how to handle the scopeAnnotation: %s", scopeAnnotation.getCanonicalName())); + } + + @Override + public Scope visitNoScoping() { + if (binding instanceof LinkedKeyBinding) { + Binding childBinding = injector.getBinding(((LinkedKeyBinding) binding).getLinkedKey()); + return getScopeInstanceOrNull(injector, childBinding); + } + return null; + } + + @Override + public Scope visitEagerSingleton() { + return Scopes.SINGLETON; + } + + public Scope visitScope(Scope scope) { + return scope; + } + }); + }*/ + } diff --git a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/MojoParameter.java b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/MojoParameter.java index 2a28f48..8c37804 100644 --- a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/MojoParameter.java +++ b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/MojoParameter.java @@ -18,6 +18,7 @@ */ package org.apache.maven.api.plugin.testing; +import java.lang.annotation.Inherited; import java.lang.annotation.Repeatable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -27,6 +28,7 @@ */ @Retention(RetentionPolicy.RUNTIME) @Repeatable(MojoParameters.class) +@Inherited public @interface MojoParameter { String name(); diff --git a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/MojoParameters.java b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/MojoParameters.java index 434abe1..373c926 100644 --- a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/MojoParameters.java +++ b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/MojoParameters.java @@ -18,6 +18,7 @@ */ package org.apache.maven.api.plugin.testing; +import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -25,6 +26,7 @@ * Mojo parameters container */ @Retention(RetentionPolicy.RUNTIME) +@Inherited public @interface MojoParameters { MojoParameter[] value(); } diff --git a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/ResolverExpressionEvaluatorStub.java b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/ResolverExpressionEvaluatorStub.java index 4461071..1ca785d 100644 --- a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/ResolverExpressionEvaluatorStub.java +++ b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/ResolverExpressionEvaluatorStub.java @@ -21,11 +21,9 @@ import java.io.File; import java.util.Map; -import org.codehaus.plexus.PlexusTestCase; import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException; import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator; import org.codehaus.plexus.component.configurator.expression.TypeAwareExpressionEvaluator; -import org.codehaus.plexus.testing.PlexusExtension; import org.eclipse.aether.repository.LocalRepository; /** @@ -81,14 +79,14 @@ public Object evaluate(String expr, Class type) throws ExpressionEvaluationEx return expression.contains("$$") ? expression.replaceAll("\\$\\$", "\\$") : expression; } else { if ("basedir".equals(expression) || "project.basedir".equals(expression)) { - value = PlexusExtension.getBasedir(); + value = MojoExtension.getBasedir(); } else if (expression.startsWith("basedir") || expression.startsWith("project.basedir")) { int pathSeparator = expression.indexOf("/"); if (pathSeparator > 0) { - value = PlexusTestCase.getBasedir() + expression.substring(pathSeparator); + value = MojoExtension.getBasedir() + expression.substring(pathSeparator); } } else if ("localRepository".equals(expression)) { - File localRepo = new File(PlexusTestCase.getBasedir(), "target/local-repo"); + File localRepo = new File(MojoExtension.getBasedir(), "target/local-repo"); return new LocalRepository("file://" + localRepo.getAbsolutePath()); } if (value == null && properties != null && properties.containsKey(expression)) { @@ -109,12 +107,12 @@ private String stripTokens(String expr) { /** {@inheritDoc} */ @Override public File alignToBaseDirectory(File file) { - if (file.getAbsolutePath().startsWith(PlexusExtension.getBasedir())) { + if (file.getAbsolutePath().startsWith(MojoExtension.getBasedir())) { return file; } else if (file.isAbsolute()) { return file; } else { - return new File(PlexusExtension.getBasedir(), file.getPath()); + return new File(MojoExtension.getBasedir(), file.getPath()); } } } diff --git a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/stubs/ArtifactStub.java b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/stubs/ArtifactStub.java index 59be14f..b9b6416 100644 --- a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/stubs/ArtifactStub.java +++ b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/stubs/ArtifactStub.java @@ -23,9 +23,11 @@ import org.apache.maven.api.Artifact; import org.apache.maven.api.ArtifactCoordinate; import org.apache.maven.api.Version; -import org.apache.maven.api.VersionRange; +import org.apache.maven.api.VersionConstraint; import org.apache.maven.api.annotations.Nonnull; import org.apache.maven.internal.impl.DefaultVersionParser; +import org.apache.maven.repository.internal.DefaultModelVersionParser; +import org.eclipse.aether.util.version.GenericVersionScheme; /** * @@ -35,6 +37,7 @@ public class ArtifactStub implements Artifact { private String artifactId; private String classifier; private String version; + private String baseVersion; private String extension; public ArtifactStub() { @@ -86,13 +89,21 @@ public void setClassifier(String classifier) { @Nonnull @Override public Version getVersion() { - return new DefaultVersionParser().parseVersion(version); + return getParser().parseVersion(version); } public void setVersion(String version) { this.version = version; } + public Version getBaseVersion() { + return getParser().parseVersion(baseVersion != null ? baseVersion : version); + } + + public void setBaseVersion(String baseVersion) { + this.baseVersion = baseVersion; + } + @Nonnull @Override public String getExtension() { @@ -127,8 +138,8 @@ public String getClassifier() { } @Override - public VersionRange getVersion() { - return new DefaultVersionParser().parseVersionRange(version); + public VersionConstraint getVersion() { + return getParser().parseVersionConstraint(version); } @Override @@ -169,4 +180,8 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash(groupId, artifactId, classifier, version, extension); } + + private static DefaultVersionParser getParser() { + return new DefaultVersionParser(new DefaultModelVersionParser(new GenericVersionScheme())); + } } diff --git a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/stubs/MojoExecutionStub.java b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/stubs/MojoExecutionStub.java index a2d2b5b..2619b75 100644 --- a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/stubs/MojoExecutionStub.java +++ b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/stubs/MojoExecutionStub.java @@ -21,24 +21,28 @@ import java.util.Optional; import org.apache.maven.api.MojoExecution; -import org.apache.maven.api.model.Plugin; +import org.apache.maven.api.Plugin; +import org.apache.maven.api.model.PluginExecution; +import org.apache.maven.api.plugin.descriptor.MojoDescriptor; import org.apache.maven.api.xml.XmlNode; /** * Stub for {@link MojoExecution}. */ public class MojoExecutionStub implements MojoExecution { - private final String artifactId; - private final String executionId; - private final String goal; - private final XmlNode dom; + private String executionId; + private String goal; + private XmlNode dom; + private Plugin plugin = new PluginStub(); + private PluginExecution model; + private MojoDescriptor descriptor; + private String lifecyclePhase; - public MojoExecutionStub(String artifactId, String executionId, String goal) { - this(artifactId, executionId, goal, null); + public MojoExecutionStub(String executionId, String goal) { + this(executionId, goal, null); } - public MojoExecutionStub(String artifactId, String executionId, String goal, XmlNode dom) { - this.artifactId = artifactId; + public MojoExecutionStub(String executionId, String goal, XmlNode dom) { this.executionId = executionId; this.goal = goal; this.dom = dom; @@ -46,7 +50,22 @@ public MojoExecutionStub(String artifactId, String executionId, String goal, Xml @Override public Plugin getPlugin() { - return Plugin.newBuilder().artifactId(artifactId).build(); + return plugin; + } + + @Override + public PluginExecution getModel() { + return model; + } + + @Override + public MojoDescriptor getDescriptor() { + return descriptor; + } + + @Override + public String getLifecyclePhase() { + return lifecyclePhase; } @Override @@ -63,4 +82,32 @@ public String getGoal() { public Optional getConfiguration() { return Optional.ofNullable(dom); } + + public void setExecutionId(String executionId) { + this.executionId = executionId; + } + + public void setGoal(String goal) { + this.goal = goal; + } + + public void setDom(XmlNode dom) { + this.dom = dom; + } + + public void setPlugin(Plugin plugin) { + this.plugin = plugin; + } + + public void setModel(PluginExecution model) { + this.model = model; + } + + public void setDescriptor(MojoDescriptor descriptor) { + this.descriptor = descriptor; + } + + public void setLifecyclePhase(String lifecyclePhase) { + this.lifecyclePhase = lifecyclePhase; + } } diff --git a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/stubs/PluginStub.java b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/stubs/PluginStub.java new file mode 100644 index 0000000..b7b4cfc --- /dev/null +++ b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/stubs/PluginStub.java @@ -0,0 +1,102 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.maven.api.plugin.testing.stubs; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.apache.maven.api.Artifact; +import org.apache.maven.api.Dependency; +import org.apache.maven.api.Plugin; +import org.apache.maven.api.plugin.descriptor.PluginDescriptor; +import org.apache.maven.api.plugin.descriptor.lifecycle.Lifecycle; + +public class PluginStub implements Plugin { + + org.apache.maven.api.model.Plugin model; + PluginDescriptor descriptor; + List lifecycles = Collections.emptyList(); + ClassLoader classLoader; + Artifact artifact; + List dependencies = Collections.emptyList(); + Map dependenciesMap = Collections.emptyMap(); + + @Override + public org.apache.maven.api.model.Plugin getModel() { + return model; + } + + public void setModel(org.apache.maven.api.model.Plugin model) { + this.model = model; + } + + @Override + public PluginDescriptor getDescriptor() { + return descriptor; + } + + public void setDescriptor(PluginDescriptor descriptor) { + this.descriptor = descriptor; + } + + @Override + public List getLifecycles() { + return lifecycles; + } + + public void setLifecycles(List lifecycles) { + this.lifecycles = lifecycles; + } + + @Override + public ClassLoader getClassLoader() { + return classLoader; + } + + public void setClassLoader(ClassLoader classLoader) { + this.classLoader = classLoader; + } + + @Override + public Artifact getArtifact() { + return artifact; + } + + public void setArtifact(Artifact artifact) { + this.artifact = artifact; + } + + @Override + public List getDependencies() { + return dependencies; + } + + public void setDependencies(List dependencies) { + this.dependencies = dependencies; + } + + public Map getDependenciesMap() { + return dependenciesMap; + } + + public void setDependenciesMap(Map dependenciesMap) { + this.dependenciesMap = dependenciesMap; + } +} diff --git a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/stubs/ProjectStub.java b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/stubs/ProjectStub.java index b3624cf..450cd1a 100644 --- a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/stubs/ProjectStub.java +++ b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/stubs/ProjectStub.java @@ -18,18 +18,16 @@ */ package org.apache.maven.api.plugin.testing.stubs; -import java.io.File; import java.nio.file.Path; -import java.util.Collections; -import java.util.List; -import java.util.Optional; - -import org.apache.maven.api.Artifact; -import org.apache.maven.api.DependencyCoordinate; -import org.apache.maven.api.Project; -import org.apache.maven.api.RemoteRepository; +import java.util.*; + +import org.apache.maven.api.*; import org.apache.maven.api.annotations.Nonnull; import org.apache.maven.api.model.Model; +import org.apache.maven.api.model.PluginContainer; +import org.apache.maven.internal.impl.DefaultVersionParser; +import org.apache.maven.repository.internal.DefaultModelVersionParser; +import org.eclipse.aether.util.version.GenericVersionScheme; /** * @author Olivier Lamy @@ -40,10 +38,11 @@ public class ProjectStub implements Project { private Model model = Model.newInstance(); private Path basedir; - private File pomPath; + private Path pomPath; private boolean topProject; - private Artifact artifact; private Path rootDirectory; + private Map properties = new HashMap<>(); + private Artifact mainArtifact; public void setModel(Model model) { this.model = model; @@ -73,14 +72,59 @@ public String getName() { @Nonnull @Override - public String getPackaging() { - return model.getPackaging(); + public Packaging getPackaging() { + return new Packaging() { + @Override + public String id() { + return model.getPackaging(); + } + + @Override + public Type type() { + return new Type() { + @Override + public String id() { + return model.getPackaging(); + } + + @Override + public Language getLanguage() { + return null; + } + + @Override + public String getExtension() { + return model.getPackaging(); + } + + @Override + public String getClassifier() { + return ""; + } + + @Override + public boolean isIncludesDependencies() { + return false; + } + + @Override + public Set getPathTypes() { + return Set.of(); + } + }; + } + + @Override + public Map plugins() { + return Map.of(); + } + }; } - @Nonnull @Override - public Artifact getArtifact() { - return artifact; + public List getArtifacts() { + Artifact pomArtifact = new ArtifactStub(getGroupId(), getArtifactId(), "", getVersion(), "pom"); + return mainArtifact != null ? Arrays.asList(pomArtifact, mainArtifact) : Arrays.asList(pomArtifact); } @Nonnull @@ -91,8 +135,8 @@ public Model getModel() { @Nonnull @Override - public Optional getPomPath() { - return Optional.ofNullable(pomPath).map(File::toPath); + public Path getPomPath() { + return pomPath; } @Nonnull @@ -108,34 +152,19 @@ public List getManagedDependencies() { } @Override - public Optional getBasedir() { - return Optional.ofNullable(basedir); + public Path getBasedir() { + return basedir; } public void setBasedir(Path basedir) { this.basedir = basedir; } - @Override - public boolean isExecutionRoot() { - return isTopProject(); - } - @Override public Optional getParent() { return Optional.empty(); } - @Override - public List getRemoteProjectRepositories() { - return Collections.emptyList(); - } - - @Override - public List getRemotePluginRepositories() { - return Collections.emptyList(); - } - @Override public boolean isTopProject() { return topProject; @@ -171,11 +200,11 @@ public void setPackaging(String packaging) { model = model.withPackaging(packaging); } - public void setArtifact(Artifact artifact) { - this.artifact = artifact; + public void setMainArtifact(Artifact mainArtifact) { + this.mainArtifact = mainArtifact; } - public void setPomPath(File pomPath) { + public void setPomPath(Path pomPath) { this.pomPath = pomPath; } @@ -190,4 +219,51 @@ public void setMavenModel(org.apache.maven.model.Model model) { public void setRootDirectory(Path rootDirectory) { this.rootDirectory = rootDirectory; } + + public void addProperty(String key, String value) { + properties.put(key, value); + } + + class ProjectArtifact implements Artifact { + @Override + public String getGroupId() { + return ProjectStub.this.getGroupId(); + } + + @Override + public String getArtifactId() { + return ProjectStub.this.getArtifactId(); + } + + @Override + public Version getVersion() { + return new DefaultVersionParser(new DefaultModelVersionParser(new GenericVersionScheme())) + .parseVersion(ProjectStub.this.getVersion()); + } + + @Override + public Version getBaseVersion() { + return null; + } + + @Override + public String getClassifier() { + return ""; + } + + @Override + public String getExtension() { + return "pom"; + } + + @Override + public boolean isSnapshot() { + return false; + } + + @Override + public ArtifactCoordinate toCoordinate() { + return null; + } + } } diff --git a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/stubs/RepositorySystemSupplier.java b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/stubs/RepositorySystemSupplier.java new file mode 100644 index 0000000..0e709b3 --- /dev/null +++ b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/stubs/RepositorySystemSupplier.java @@ -0,0 +1,1069 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.maven.api.plugin.testing.stubs; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Supplier; + +import org.apache.maven.api.services.ModelBuilder; +import org.apache.maven.internal.impl.DefaultModelUrlNormalizer; +import org.apache.maven.internal.impl.DefaultModelVersionParser; +import org.apache.maven.internal.impl.DefaultModelXmlFactory; +import org.apache.maven.internal.impl.DefaultPluginConfigurationExpander; +import org.apache.maven.internal.impl.DefaultSuperPomProvider; +import org.apache.maven.internal.impl.DefaultUrlNormalizer; +import org.apache.maven.internal.impl.model.BuildModelTransformer; +import org.apache.maven.internal.impl.model.DefaultDependencyManagementImporter; +import org.apache.maven.internal.impl.model.DefaultDependencyManagementInjector; +import org.apache.maven.internal.impl.model.DefaultInheritanceAssembler; +import org.apache.maven.internal.impl.model.DefaultModelBuilder; +import org.apache.maven.internal.impl.model.DefaultModelInterpolator; +import org.apache.maven.internal.impl.model.DefaultModelNormalizer; +import org.apache.maven.internal.impl.model.DefaultModelPathTranslator; +import org.apache.maven.internal.impl.model.DefaultModelProcessor; +import org.apache.maven.internal.impl.model.DefaultModelValidator; +import org.apache.maven.internal.impl.model.DefaultModelVersionProcessor; +import org.apache.maven.internal.impl.model.DefaultPathTranslator; +import org.apache.maven.internal.impl.model.DefaultPluginManagementInjector; +import org.apache.maven.internal.impl.model.DefaultProfileInjector; +import org.apache.maven.internal.impl.model.DefaultProfileSelector; +import org.apache.maven.internal.impl.model.DefaultRootLocator; +import org.apache.maven.internal.impl.model.ProfileActivationFilePathInterpolator; +import org.apache.maven.internal.impl.resolver.DefaultArtifactDescriptorReader; +import org.apache.maven.internal.impl.resolver.DefaultVersionRangeResolver; +import org.apache.maven.internal.impl.resolver.DefaultVersionResolver; +import org.apache.maven.internal.impl.resolver.MavenArtifactRelocationSource; +import org.apache.maven.internal.impl.resolver.PluginsMetadataGeneratorFactory; +import org.apache.maven.internal.impl.resolver.SnapshotMetadataGeneratorFactory; +import org.apache.maven.internal.impl.resolver.VersionsMetadataGeneratorFactory; +import org.apache.maven.internal.impl.resolver.relocation.DistributionManagementArtifactRelocationSource; +import org.apache.maven.internal.impl.resolver.relocation.UserPropertiesArtifactRelocationSource; +import org.eclipse.aether.RepositoryListener; +import org.eclipse.aether.RepositorySystem; +import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory; +import org.eclipse.aether.impl.ArtifactDescriptorReader; +import org.eclipse.aether.impl.ArtifactResolver; +import org.eclipse.aether.impl.DependencyCollector; +import org.eclipse.aether.impl.Deployer; +import org.eclipse.aether.impl.Installer; +import org.eclipse.aether.impl.LocalRepositoryProvider; +import org.eclipse.aether.impl.MetadataGeneratorFactory; +import org.eclipse.aether.impl.MetadataResolver; +import org.eclipse.aether.impl.OfflineController; +import org.eclipse.aether.impl.RemoteRepositoryFilterManager; +import org.eclipse.aether.impl.RemoteRepositoryManager; +import org.eclipse.aether.impl.RepositoryConnectorProvider; +import org.eclipse.aether.impl.RepositoryEventDispatcher; +import org.eclipse.aether.impl.RepositorySystemLifecycle; +import org.eclipse.aether.impl.UpdateCheckManager; +import org.eclipse.aether.impl.UpdatePolicyAnalyzer; +import org.eclipse.aether.impl.VersionRangeResolver; +import org.eclipse.aether.impl.VersionResolver; +import org.eclipse.aether.internal.impl.*; +import org.eclipse.aether.internal.impl.checksum.DefaultChecksumAlgorithmFactorySelector; +import org.eclipse.aether.internal.impl.checksum.Md5ChecksumAlgorithmFactory; +import org.eclipse.aether.internal.impl.checksum.Sha1ChecksumAlgorithmFactory; +import org.eclipse.aether.internal.impl.checksum.Sha256ChecksumAlgorithmFactory; +import org.eclipse.aether.internal.impl.checksum.Sha512ChecksumAlgorithmFactory; +import org.eclipse.aether.internal.impl.checksum.SparseDirectoryTrustedChecksumsSource; +import org.eclipse.aether.internal.impl.checksum.SummaryFileTrustedChecksumsSource; +import org.eclipse.aether.internal.impl.checksum.TrustedToProvidedChecksumsSourceAdapter; +import org.eclipse.aether.internal.impl.collect.DefaultDependencyCollector; +import org.eclipse.aether.internal.impl.collect.DependencyCollectorDelegate; +import org.eclipse.aether.internal.impl.collect.bf.BfDependencyCollector; +import org.eclipse.aether.internal.impl.collect.df.DfDependencyCollector; +import org.eclipse.aether.internal.impl.filter.DefaultRemoteRepositoryFilterManager; +import org.eclipse.aether.internal.impl.filter.GroupIdRemoteRepositoryFilterSource; +import org.eclipse.aether.internal.impl.filter.PrefixesRemoteRepositoryFilterSource; +import org.eclipse.aether.internal.impl.resolution.TrustedChecksumsArtifactResolverPostProcessor; +import org.eclipse.aether.internal.impl.synccontext.DefaultSyncContextFactory; +import org.eclipse.aether.internal.impl.synccontext.named.NameMapper; +import org.eclipse.aether.internal.impl.synccontext.named.NameMappers; +import org.eclipse.aether.internal.impl.synccontext.named.NamedLockFactoryAdapterFactory; +import org.eclipse.aether.internal.impl.synccontext.named.NamedLockFactoryAdapterFactoryImpl; +import org.eclipse.aether.internal.impl.transport.http.DefaultChecksumExtractor; +import org.eclipse.aether.internal.impl.transport.http.Nx2ChecksumExtractor; +import org.eclipse.aether.internal.impl.transport.http.XChecksumExtractor; +import org.eclipse.aether.named.NamedLockFactory; +import org.eclipse.aether.named.providers.FileLockNamedLockFactory; +import org.eclipse.aether.named.providers.LocalReadWriteLockNamedLockFactory; +import org.eclipse.aether.named.providers.LocalSemaphoreNamedLockFactory; +import org.eclipse.aether.named.providers.NoopNamedLockFactory; +import org.eclipse.aether.spi.artifact.ArtifactPredicateFactory; +import org.eclipse.aether.spi.artifact.decorator.ArtifactDecoratorFactory; +import org.eclipse.aether.spi.artifact.generator.ArtifactGeneratorFactory; +import org.eclipse.aether.spi.checksums.ProvidedChecksumsSource; +import org.eclipse.aether.spi.checksums.TrustedChecksumsSource; +import org.eclipse.aether.spi.connector.RepositoryConnectorFactory; +import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory; +import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactorySelector; +import org.eclipse.aether.spi.connector.checksum.ChecksumPolicyProvider; +import org.eclipse.aether.spi.connector.filter.RemoteRepositoryFilterSource; +import org.eclipse.aether.spi.connector.layout.RepositoryLayoutFactory; +import org.eclipse.aether.spi.connector.layout.RepositoryLayoutProvider; +import org.eclipse.aether.spi.connector.transport.TransporterFactory; +import org.eclipse.aether.spi.connector.transport.TransporterProvider; +import org.eclipse.aether.spi.connector.transport.http.ChecksumExtractor; +import org.eclipse.aether.spi.connector.transport.http.ChecksumExtractorStrategy; +import org.eclipse.aether.spi.io.ChecksumProcessor; +import org.eclipse.aether.spi.io.PathProcessor; +import org.eclipse.aether.spi.localrepo.LocalRepositoryManagerFactory; +import org.eclipse.aether.spi.resolution.ArtifactResolverPostProcessor; +import org.eclipse.aether.spi.synccontext.SyncContextFactory; +import org.eclipse.aether.transport.apache.ApacheTransporterFactory; +import org.eclipse.aether.transport.file.FileTransporterFactory; +import org.eclipse.aether.util.version.GenericVersionScheme; +import org.eclipse.aether.version.VersionScheme; + +/** + * A simple memorizing {@link Supplier} of {@link RepositorySystem} instance, that on first call + * supplies lazily constructed instance, and on each subsequent call same instance. Hence, this instance should be + * thrown away immediately once repository system was created and there is no need for more instances. If new + * repository system instance needed, new instance of this class must be created. For proper shut down of returned + * repository system instance(s) use {@link RepositorySystem#shutdown()} method on supplied instance(s). + *

+ * Since Resolver 2.0 this class offers access to various components via public getters, and allows even partial object + * graph construction. + *

+ * Extend this class {@code createXXX()} methods and override to customize, if needed. The contract of this class makes + * sure that these (potentially overridden) methods are invoked only once, and instance created by those methods are + * memorized and kept as long as supplier instance is kept open. + *

+ * This class is not thread safe and must be used from one thread only, while the constructed {@link RepositorySystem} + * is thread safe. + *

+ * Important: Given the instance of supplier memorizes the supplier {@link RepositorySystem} instance it supplies, + * their lifecycle is shared as well: once supplied repository system is shut-down, this instance becomes closed as + * well. Any subsequent {@code getXXX} method invocation attempt will fail with {@link IllegalStateException}. + */ +public class RepositorySystemSupplier implements Supplier { + private final AtomicBoolean closed = new AtomicBoolean(false); + + public RepositorySystemSupplier() {} + + private void checkClosed() { + if (closed.get()) { + throw new IllegalStateException("Supplier is closed"); + } + } + + private PathProcessor pathProcessor; + + public final PathProcessor getPathProcessor() { + checkClosed(); + if (pathProcessor == null) { + pathProcessor = createPathProcessor(); + } + return pathProcessor; + } + + protected PathProcessor createPathProcessor() { + return new DefaultPathProcessor(); + } + + private ChecksumProcessor checksumProcessor; + + public final ChecksumProcessor getChecksumProcessor() { + checkClosed(); + if (checksumProcessor == null) { + checksumProcessor = createChecksumProcessor(); + } + return checksumProcessor; + } + + protected ChecksumProcessor createChecksumProcessor() { + return new DefaultChecksumProcessor(getPathProcessor()); + } + + private TrackingFileManager trackingFileManager; + + public final TrackingFileManager getTrackingFileManager() { + checkClosed(); + if (trackingFileManager == null) { + trackingFileManager = createTrackingFileManager(); + } + return trackingFileManager; + } + + protected TrackingFileManager createTrackingFileManager() { + return new DefaultTrackingFileManager(); + } + + private LocalPathComposer localPathComposer; + + public final LocalPathComposer getLocalPathComposer() { + checkClosed(); + if (localPathComposer == null) { + localPathComposer = createLocalPathComposer(); + } + return localPathComposer; + } + + protected LocalPathComposer createLocalPathComposer() { + return new DefaultLocalPathComposer(); + } + + private LocalPathPrefixComposerFactory localPathPrefixComposerFactory; + + public final LocalPathPrefixComposerFactory getLocalPathPrefixComposerFactory() { + checkClosed(); + if (localPathPrefixComposerFactory == null) { + localPathPrefixComposerFactory = createLocalPathPrefixComposerFactory(); + } + return localPathPrefixComposerFactory; + } + + protected LocalPathPrefixComposerFactory createLocalPathPrefixComposerFactory() { + return new DefaultLocalPathPrefixComposerFactory(); + } + + private RepositorySystemLifecycle repositorySystemLifecycle; + + public final RepositorySystemLifecycle getRepositorySystemLifecycle() { + checkClosed(); + if (repositorySystemLifecycle == null) { + repositorySystemLifecycle = createRepositorySystemLifecycle(); + repositorySystemLifecycle.addOnSystemEndedHandler(() -> closed.set(true)); + } + return repositorySystemLifecycle; + } + + protected RepositorySystemLifecycle createRepositorySystemLifecycle() { + return new DefaultRepositorySystemLifecycle(); + } + + private OfflineController offlineController; + + public final OfflineController getOfflineController() { + checkClosed(); + if (offlineController == null) { + offlineController = createOfflineController(); + } + return offlineController; + } + + protected OfflineController createOfflineController() { + return new DefaultOfflineController(); + } + + private UpdatePolicyAnalyzer updatePolicyAnalyzer; + + public final UpdatePolicyAnalyzer getUpdatePolicyAnalyzer() { + checkClosed(); + if (updatePolicyAnalyzer == null) { + updatePolicyAnalyzer = createUpdatePolicyAnalyzer(); + } + return updatePolicyAnalyzer; + } + + protected UpdatePolicyAnalyzer createUpdatePolicyAnalyzer() { + return new DefaultUpdatePolicyAnalyzer(); + } + + private ChecksumPolicyProvider checksumPolicyProvider; + + public final ChecksumPolicyProvider getChecksumPolicyProvider() { + checkClosed(); + if (checksumPolicyProvider == null) { + checksumPolicyProvider = createChecksumPolicyProvider(); + } + return checksumPolicyProvider; + } + + protected ChecksumPolicyProvider createChecksumPolicyProvider() { + return new DefaultChecksumPolicyProvider(); + } + + private UpdateCheckManager updateCheckManager; + + public final UpdateCheckManager getUpdateCheckManager() { + checkClosed(); + if (updateCheckManager == null) { + updateCheckManager = createUpdateCheckManager(); + } + return updateCheckManager; + } + + protected UpdateCheckManager createUpdateCheckManager() { + return new DefaultUpdateCheckManager(getTrackingFileManager(), getUpdatePolicyAnalyzer(), getPathProcessor()); + } + + private Map namedLockFactories; + + public final Map getNamedLockFactories() { + checkClosed(); + if (namedLockFactories == null) { + namedLockFactories = createNamedLockFactories(); + } + return namedLockFactories; + } + + protected Map createNamedLockFactories() { + HashMap result = new HashMap<>(); + result.put(NoopNamedLockFactory.NAME, new NoopNamedLockFactory()); + result.put(LocalReadWriteLockNamedLockFactory.NAME, new LocalReadWriteLockNamedLockFactory()); + result.put(LocalSemaphoreNamedLockFactory.NAME, new LocalSemaphoreNamedLockFactory()); + result.put(FileLockNamedLockFactory.NAME, new FileLockNamedLockFactory()); + return result; + } + + private Map nameMappers; + + public final Map getNameMappers() { + checkClosed(); + if (nameMappers == null) { + nameMappers = createNameMappers(); + } + return nameMappers; + } + + protected Map createNameMappers() { + HashMap result = new HashMap<>(); + result.put(NameMappers.STATIC_NAME, NameMappers.staticNameMapper()); + result.put(NameMappers.GAV_NAME, NameMappers.gavNameMapper()); + result.put(NameMappers.DISCRIMINATING_NAME, NameMappers.discriminatingNameMapper()); + result.put(NameMappers.FILE_GAV_NAME, NameMappers.fileGavNameMapper()); + result.put(NameMappers.FILE_HGAV_NAME, NameMappers.fileHashingGavNameMapper()); + return result; + } + + private NamedLockFactoryAdapterFactory namedLockFactoryAdapterFactory; + + public final NamedLockFactoryAdapterFactory getNamedLockFactoryAdapterFactory() { + checkClosed(); + if (namedLockFactoryAdapterFactory == null) { + namedLockFactoryAdapterFactory = createNamedLockFactoryAdapterFactory(); + } + return namedLockFactoryAdapterFactory; + } + + protected NamedLockFactoryAdapterFactory createNamedLockFactoryAdapterFactory() { + return new NamedLockFactoryAdapterFactoryImpl( + getNamedLockFactories(), getNameMappers(), getRepositorySystemLifecycle()); + } + + private SyncContextFactory syncContextFactory; + + public final SyncContextFactory getSyncContextFactory() { + checkClosed(); + if (syncContextFactory == null) { + syncContextFactory = createSyncContextFactory(); + } + return syncContextFactory; + } + + protected SyncContextFactory createSyncContextFactory() { + return new DefaultSyncContextFactory(getNamedLockFactoryAdapterFactory()); + } + + private Map checksumAlgorithmFactories; + + public final Map getChecksumAlgorithmFactories() { + checkClosed(); + if (checksumAlgorithmFactories == null) { + checksumAlgorithmFactories = createChecksumAlgorithmFactories(); + } + return checksumAlgorithmFactories; + } + + protected Map createChecksumAlgorithmFactories() { + HashMap result = new HashMap<>(); + result.put(Sha512ChecksumAlgorithmFactory.NAME, new Sha512ChecksumAlgorithmFactory()); + result.put(Sha256ChecksumAlgorithmFactory.NAME, new Sha256ChecksumAlgorithmFactory()); + result.put(Sha1ChecksumAlgorithmFactory.NAME, new Sha1ChecksumAlgorithmFactory()); + result.put(Md5ChecksumAlgorithmFactory.NAME, new Md5ChecksumAlgorithmFactory()); + return result; + } + + private ChecksumAlgorithmFactorySelector checksumAlgorithmFactorySelector; + + public final ChecksumAlgorithmFactorySelector getChecksumAlgorithmFactorySelector() { + checkClosed(); + if (checksumAlgorithmFactorySelector == null) { + checksumAlgorithmFactorySelector = createChecksumAlgorithmFactorySelector(); + } + return checksumAlgorithmFactorySelector; + } + + protected ChecksumAlgorithmFactorySelector createChecksumAlgorithmFactorySelector() { + return new DefaultChecksumAlgorithmFactorySelector(getChecksumAlgorithmFactories()); + } + + private ArtifactPredicateFactory artifactPredicateFactory; + + public final ArtifactPredicateFactory getArtifactPredicateFactory() { + checkClosed(); + if (artifactPredicateFactory == null) { + artifactPredicateFactory = createArtifactPredicateFactory(); + } + return artifactPredicateFactory; + } + + protected ArtifactPredicateFactory createArtifactPredicateFactory() { + return new DefaultArtifactPredicateFactory(getChecksumAlgorithmFactorySelector()); + } + + private Map repositoryLayoutFactories; + + public final Map getRepositoryLayoutFactories() { + checkClosed(); + if (repositoryLayoutFactories == null) { + repositoryLayoutFactories = createRepositoryLayoutFactories(); + } + return repositoryLayoutFactories; + } + + protected Map createRepositoryLayoutFactories() { + HashMap result = new HashMap<>(); + result.put( + Maven2RepositoryLayoutFactory.NAME, + new Maven2RepositoryLayoutFactory( + getChecksumAlgorithmFactorySelector(), getArtifactPredicateFactory())); + return result; + } + + private RepositoryLayoutProvider repositoryLayoutProvider; + + public final RepositoryLayoutProvider getRepositoryLayoutProvider() { + checkClosed(); + if (repositoryLayoutProvider == null) { + repositoryLayoutProvider = createRepositoryLayoutProvider(); + } + return repositoryLayoutProvider; + } + + protected RepositoryLayoutProvider createRepositoryLayoutProvider() { + return new DefaultRepositoryLayoutProvider(getRepositoryLayoutFactories()); + } + + private LocalRepositoryProvider localRepositoryProvider; + + public final LocalRepositoryProvider getLocalRepositoryProvider() { + checkClosed(); + if (localRepositoryProvider == null) { + localRepositoryProvider = createLocalRepositoryProvider(); + } + return localRepositoryProvider; + } + + protected LocalRepositoryProvider createLocalRepositoryProvider() { + LocalPathComposer localPathComposer = getLocalPathComposer(); + HashMap localRepositoryProviders = new HashMap<>(2); + localRepositoryProviders.put( + SimpleLocalRepositoryManagerFactory.NAME, new SimpleLocalRepositoryManagerFactory(localPathComposer)); + localRepositoryProviders.put( + EnhancedLocalRepositoryManagerFactory.NAME, + new EnhancedLocalRepositoryManagerFactory( + localPathComposer, getTrackingFileManager(), getLocalPathPrefixComposerFactory())); + return new DefaultLocalRepositoryProvider(localRepositoryProviders); + } + + private RemoteRepositoryManager remoteRepositoryManager; + + public final RemoteRepositoryManager getRemoteRepositoryManager() { + checkClosed(); + if (remoteRepositoryManager == null) { + remoteRepositoryManager = createRemoteRepositoryManager(); + } + return remoteRepositoryManager; + } + + protected RemoteRepositoryManager createRemoteRepositoryManager() { + return new DefaultRemoteRepositoryManager(getUpdatePolicyAnalyzer(), getChecksumPolicyProvider()); + } + + private Map remoteRepositoryFilterSources; + + public final Map getRemoteRepositoryFilterSources() { + checkClosed(); + if (remoteRepositoryFilterSources == null) { + remoteRepositoryFilterSources = createRemoteRepositoryFilterSources(); + } + return remoteRepositoryFilterSources; + } + + protected Map createRemoteRepositoryFilterSources() { + HashMap result = new HashMap<>(); + result.put( + GroupIdRemoteRepositoryFilterSource.NAME, + new GroupIdRemoteRepositoryFilterSource(getRepositorySystemLifecycle())); + result.put( + PrefixesRemoteRepositoryFilterSource.NAME, + new PrefixesRemoteRepositoryFilterSource(getRepositoryLayoutProvider())); + return result; + } + + private RemoteRepositoryFilterManager remoteRepositoryFilterManager; + + public final RemoteRepositoryFilterManager getRemoteRepositoryFilterManager() { + checkClosed(); + if (remoteRepositoryFilterManager == null) { + remoteRepositoryFilterManager = createRemoteRepositoryFilterManager(); + } + return remoteRepositoryFilterManager; + } + + protected RemoteRepositoryFilterManager createRemoteRepositoryFilterManager() { + return new DefaultRemoteRepositoryFilterManager(getRemoteRepositoryFilterSources()); + } + + private Map repositoryListeners; + + public final Map getRepositoryListeners() { + checkClosed(); + if (repositoryListeners == null) { + repositoryListeners = createRepositoryListeners(); + } + return repositoryListeners; + } + + protected Map createRepositoryListeners() { + return new HashMap<>(); + } + + private RepositoryEventDispatcher repositoryEventDispatcher; + + public final RepositoryEventDispatcher getRepositoryEventDispatcher() { + checkClosed(); + if (repositoryEventDispatcher == null) { + repositoryEventDispatcher = createRepositoryEventDispatcher(); + } + return repositoryEventDispatcher; + } + + protected RepositoryEventDispatcher createRepositoryEventDispatcher() { + return new DefaultRepositoryEventDispatcher(getRepositoryListeners()); + } + + private Map trustedChecksumsSources; + + public final Map getTrustedChecksumsSources() { + checkClosed(); + if (trustedChecksumsSources == null) { + trustedChecksumsSources = createTrustedChecksumsSources(); + } + return trustedChecksumsSources; + } + + protected Map createTrustedChecksumsSources() { + HashMap result = new HashMap<>(); + result.put( + SparseDirectoryTrustedChecksumsSource.NAME, + new SparseDirectoryTrustedChecksumsSource(getChecksumProcessor(), getLocalPathComposer())); + result.put( + SummaryFileTrustedChecksumsSource.NAME, + new SummaryFileTrustedChecksumsSource(getLocalPathComposer(), getRepositorySystemLifecycle())); + return result; + } + + private Map providedChecksumsSources; + + public final Map getProvidedChecksumsSources() { + checkClosed(); + if (providedChecksumsSources == null) { + providedChecksumsSources = createProvidedChecksumsSources(); + } + return providedChecksumsSources; + } + + protected Map createProvidedChecksumsSources() { + HashMap result = new HashMap<>(); + result.put( + TrustedToProvidedChecksumsSourceAdapter.NAME, + new TrustedToProvidedChecksumsSourceAdapter(getTrustedChecksumsSources())); + return result; + } + + private Map checksumExtractorStrategies; + + public final Map getChecksumExtractorStrategies() { + checkClosed(); + if (checksumExtractorStrategies == null) { + checksumExtractorStrategies = createChecksumExtractorStrategies(); + } + return checksumExtractorStrategies; + } + + protected Map createChecksumExtractorStrategies() { + HashMap result = new HashMap<>(); + result.put(XChecksumExtractor.NAME, new XChecksumExtractor()); + result.put(Nx2ChecksumExtractor.NAME, new Nx2ChecksumExtractor()); + return result; + } + + private ChecksumExtractor checksumExtractor; + + public final ChecksumExtractor getChecksumExtractor() { + checkClosed(); + if (checksumExtractor == null) { + checksumExtractor = createChecksumExtractor(); + } + return checksumExtractor; + } + + protected ChecksumExtractor createChecksumExtractor() { + return new DefaultChecksumExtractor(getChecksumExtractorStrategies()); + } + + private Map transporterFactories; + + public final Map getTransporterFactories() { + checkClosed(); + if (transporterFactories == null) { + transporterFactories = createTransporterFactories(); + } + return transporterFactories; + } + + protected Map createTransporterFactories() { + HashMap result = new HashMap<>(); + result.put(FileTransporterFactory.NAME, new FileTransporterFactory()); + result.put( + ApacheTransporterFactory.NAME, + new ApacheTransporterFactory(getChecksumExtractor(), getPathProcessor())); + return result; + } + + private TransporterProvider transporterProvider; + + public final TransporterProvider getTransporterProvider() { + checkClosed(); + if (transporterProvider == null) { + transporterProvider = createTransporterProvider(); + } + return transporterProvider; + } + + protected TransporterProvider createTransporterProvider() { + return new DefaultTransporterProvider(getTransporterFactories()); + } + + private BasicRepositoryConnectorFactory basicRepositoryConnectorFactory; + + public final BasicRepositoryConnectorFactory getBasicRepositoryConnectorFactory() { + checkClosed(); + if (basicRepositoryConnectorFactory == null) { + basicRepositoryConnectorFactory = createBasicRepositoryConnectorFactory(); + } + return basicRepositoryConnectorFactory; + } + + protected BasicRepositoryConnectorFactory createBasicRepositoryConnectorFactory() { + return new BasicRepositoryConnectorFactory( + getTransporterProvider(), + getRepositoryLayoutProvider(), + getChecksumPolicyProvider(), + getChecksumProcessor(), + getProvidedChecksumsSources()); + } + + private Map repositoryConnectorFactories; + + public final Map getRepositoryConnectorFactories() { + checkClosed(); + if (repositoryConnectorFactories == null) { + repositoryConnectorFactories = createRepositoryConnectorFactories(); + } + return repositoryConnectorFactories; + } + + protected Map createRepositoryConnectorFactories() { + HashMap result = new HashMap<>(); + result.put(BasicRepositoryConnectorFactory.NAME, getBasicRepositoryConnectorFactory()); + return result; + } + + private RepositoryConnectorProvider repositoryConnectorProvider; + + public final RepositoryConnectorProvider getRepositoryConnectorProvider() { + checkClosed(); + if (repositoryConnectorProvider == null) { + repositoryConnectorProvider = createRepositoryConnectorProvider(); + } + return repositoryConnectorProvider; + } + + protected RepositoryConnectorProvider createRepositoryConnectorProvider() { + return new DefaultRepositoryConnectorProvider( + getRepositoryConnectorFactories(), getRemoteRepositoryFilterManager()); + } + + private Installer installer; + + public final Installer getInstaller() { + checkClosed(); + if (installer == null) { + installer = createInstaller(); + } + return installer; + } + + protected Installer createInstaller() { + return new DefaultInstaller( + getPathProcessor(), + getRepositoryEventDispatcher(), + getArtifactGeneratorFactories(), + getMetadataGeneratorFactories(), + getSyncContextFactory()); + } + + private Deployer deployer; + + public final Deployer getDeployer() { + checkClosed(); + if (deployer == null) { + deployer = createDeployer(); + } + return deployer; + } + + protected Deployer createDeployer() { + return new DefaultDeployer( + getPathProcessor(), + getRepositoryEventDispatcher(), + getRepositoryConnectorProvider(), + getRemoteRepositoryManager(), + getUpdateCheckManager(), + getArtifactGeneratorFactories(), + getMetadataGeneratorFactories(), + getSyncContextFactory(), + getOfflineController()); + } + + private Map dependencyCollectorDelegates; + + public final Map getDependencyCollectorDelegates() { + checkClosed(); + if (dependencyCollectorDelegates == null) { + dependencyCollectorDelegates = createDependencyCollectorDelegates(); + } + return dependencyCollectorDelegates; + } + + protected Map createDependencyCollectorDelegates() { + RemoteRepositoryManager remoteRepositoryManager = getRemoteRepositoryManager(); + ArtifactDescriptorReader artifactDescriptorReader = getArtifactDescriptorReader(); + VersionRangeResolver versionRangeResolver = getVersionRangeResolver(); + HashMap result = new HashMap<>(); + result.put( + DfDependencyCollector.NAME, + new DfDependencyCollector( + remoteRepositoryManager, + artifactDescriptorReader, + versionRangeResolver, + getArtifactDecoratorFactories())); + result.put( + BfDependencyCollector.NAME, + new BfDependencyCollector( + remoteRepositoryManager, + artifactDescriptorReader, + versionRangeResolver, + getArtifactDecoratorFactories())); + return result; + } + + private DependencyCollector dependencyCollector; + + public final DependencyCollector getDependencyCollector() { + checkClosed(); + if (dependencyCollector == null) { + dependencyCollector = createDependencyCollector(); + } + return dependencyCollector; + } + + protected DependencyCollector createDependencyCollector() { + return new DefaultDependencyCollector(getDependencyCollectorDelegates()); + } + + private Map artifactResolverPostProcessors; + + public final Map getArtifactResolverPostProcessors() { + checkClosed(); + if (artifactResolverPostProcessors == null) { + artifactResolverPostProcessors = createArtifactResolverPostProcessors(); + } + return artifactResolverPostProcessors; + } + + protected Map createArtifactResolverPostProcessors() { + HashMap result = new HashMap<>(); + result.put( + TrustedChecksumsArtifactResolverPostProcessor.NAME, + new TrustedChecksumsArtifactResolverPostProcessor( + getChecksumAlgorithmFactorySelector(), getTrustedChecksumsSources())); + return result; + } + + private ArtifactResolver artifactResolver; + + public final ArtifactResolver getArtifactResolver() { + checkClosed(); + if (artifactResolver == null) { + artifactResolver = createArtifactResolver(); + } + return artifactResolver; + } + + protected ArtifactResolver createArtifactResolver() { + return new DefaultArtifactResolver( + getPathProcessor(), + getRepositoryEventDispatcher(), + getVersionResolver(), + getUpdateCheckManager(), + getRepositoryConnectorProvider(), + getRemoteRepositoryManager(), + getSyncContextFactory(), + getOfflineController(), + getArtifactResolverPostProcessors(), + getRemoteRepositoryFilterManager()); + } + + private MetadataResolver metadataResolver; + + public final MetadataResolver getMetadataResolver() { + checkClosed(); + if (metadataResolver == null) { + metadataResolver = createMetadataResolver(); + } + return metadataResolver; + } + + protected MetadataResolver createMetadataResolver() { + return new DefaultMetadataResolver( + getRepositoryEventDispatcher(), + getUpdateCheckManager(), + getRepositoryConnectorProvider(), + getRemoteRepositoryManager(), + getSyncContextFactory(), + getOfflineController(), + getRemoteRepositoryFilterManager(), + getPathProcessor()); + } + + private VersionScheme versionScheme; + + public final VersionScheme getVersionScheme() { + checkClosed(); + if (versionScheme == null) { + versionScheme = createVersionScheme(); + } + return versionScheme; + } + + protected VersionScheme createVersionScheme() { + return new GenericVersionScheme(); + } + + private Map artifactGeneratorFactories; + + public final Map getArtifactGeneratorFactories() { + checkClosed(); + if (artifactGeneratorFactories == null) { + artifactGeneratorFactories = createArtifactGeneratorFactories(); + } + return artifactGeneratorFactories; + } + + protected Map createArtifactGeneratorFactories() { + // by default none, this is extension point + return new HashMap<>(); + } + + private Map artifactDecoratorFactories; + + public final Map getArtifactDecoratorFactories() { + checkClosed(); + if (artifactDecoratorFactories == null) { + artifactDecoratorFactories = createArtifactDecoratorFactories(); + } + return artifactDecoratorFactories; + } + + protected Map createArtifactDecoratorFactories() { + // by default none, this is extension point + return new HashMap<>(); + } + + // Maven provided + + private Map metadataGeneratorFactories; + + public final Map getMetadataGeneratorFactories() { + checkClosed(); + if (metadataGeneratorFactories == null) { + metadataGeneratorFactories = createMetadataGeneratorFactories(); + } + return metadataGeneratorFactories; + } + + protected Map createMetadataGeneratorFactories() { + // from maven-resolver-provider + HashMap result = new HashMap<>(); + result.put(PluginsMetadataGeneratorFactory.NAME, new PluginsMetadataGeneratorFactory()); + result.put(VersionsMetadataGeneratorFactory.NAME, new VersionsMetadataGeneratorFactory()); + result.put(SnapshotMetadataGeneratorFactory.NAME, new SnapshotMetadataGeneratorFactory()); + return result; + } + + private LinkedHashMap artifactRelocationSources; + + public final LinkedHashMap getMavenArtifactRelocationSources() { + checkClosed(); + if (artifactRelocationSources == null) { + artifactRelocationSources = createMavenArtifactRelocationSources(); + } + return artifactRelocationSources; + } + + protected LinkedHashMap createMavenArtifactRelocationSources() { + // from maven-resolver-provider + LinkedHashMap result = new LinkedHashMap<>(); + result.put(UserPropertiesArtifactRelocationSource.NAME, new UserPropertiesArtifactRelocationSource()); + result.put( + DistributionManagementArtifactRelocationSource.NAME, + new DistributionManagementArtifactRelocationSource()); + return result; + } + + private ArtifactDescriptorReader artifactDescriptorReader; + + public final ArtifactDescriptorReader getArtifactDescriptorReader() { + checkClosed(); + if (artifactDescriptorReader == null) { + artifactDescriptorReader = createArtifactDescriptorReader(); + } + return artifactDescriptorReader; + } + + protected ArtifactDescriptorReader createArtifactDescriptorReader() { + // from maven-resolver-provider + return new DefaultArtifactDescriptorReader( + getRemoteRepositoryManager(), + getVersionResolver(), + getVersionRangeResolver(), + getArtifactResolver(), + getModelBuilder(), + getRepositoryEventDispatcher(), + getMavenArtifactRelocationSources()); + } + + private VersionResolver versionResolver; + + public final VersionResolver getVersionResolver() { + checkClosed(); + if (versionResolver == null) { + versionResolver = createVersionResolver(); + } + return versionResolver; + } + + protected VersionResolver createVersionResolver() { + // from maven-resolver-provider + return new DefaultVersionResolver( + getMetadataResolver(), getSyncContextFactory(), getRepositoryEventDispatcher()); + } + + private VersionRangeResolver versionRangeResolver; + + public final VersionRangeResolver getVersionRangeResolver() { + checkClosed(); + if (versionRangeResolver == null) { + versionRangeResolver = createVersionRangeResolver(); + } + return versionRangeResolver; + } + + protected VersionRangeResolver createVersionRangeResolver() { + // from maven-resolver-provider + return new DefaultVersionRangeResolver( + getMetadataResolver(), getSyncContextFactory(), getRepositoryEventDispatcher(), getVersionScheme()); + } + + private ModelBuilder modelBuilder; + + public final ModelBuilder getModelBuilder() { + checkClosed(); + if (modelBuilder == null) { + modelBuilder = createModelBuilder(); + } + return modelBuilder; + } + + protected ModelBuilder createModelBuilder() { + // from maven-model-builder + DefaultModelProcessor modelProcessor = new DefaultModelProcessor(new DefaultModelXmlFactory(), List.of()); + return new DefaultModelBuilder( + modelProcessor, + new DefaultModelValidator(new DefaultModelVersionProcessor()), + new DefaultModelNormalizer(), + new DefaultModelInterpolator( + new DefaultPathTranslator(), new DefaultUrlNormalizer(), new DefaultRootLocator()), + new DefaultModelPathTranslator(new DefaultPathTranslator()), + new DefaultModelUrlNormalizer(new DefaultUrlNormalizer()), + new DefaultSuperPomProvider(modelProcessor), + new DefaultInheritanceAssembler(), + new DefaultProfileSelector(), + new DefaultProfileInjector(), + new DefaultPluginManagementInjector(), + new DefaultDependencyManagementInjector(), + new DefaultDependencyManagementImporter(), + (m, r, b) -> m, + new DefaultPluginConfigurationExpander(), + new ProfileActivationFilePathInterpolator(new DefaultPathTranslator(), new DefaultRootLocator()), + new BuildModelTransformer(), + new DefaultModelVersionParser(getVersionScheme())); + } + + private RepositorySystem repositorySystem; + + public final RepositorySystem getRepositorySystem() { + checkClosed(); + if (repositorySystem == null) { + repositorySystem = createRepositorySystem(); + } + return repositorySystem; + } + + protected RepositorySystem createRepositorySystem() { + return new DefaultRepositorySystem( + getVersionResolver(), + getVersionRangeResolver(), + getArtifactResolver(), + getMetadataResolver(), + getArtifactDescriptorReader(), + getDependencyCollector(), + getInstaller(), + getDeployer(), + getLocalRepositoryProvider(), + getSyncContextFactory(), + getRemoteRepositoryManager(), + getRepositorySystemLifecycle(), + getArtifactDecoratorFactories()); + } + + @Override + public RepositorySystem get() { + return getRepositorySystem(); + } +} diff --git a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/stubs/SessionStub.java b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/stubs/SessionStub.java index 2b05567..799587a 100644 --- a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/stubs/SessionStub.java +++ b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/stubs/SessionStub.java @@ -22,17 +22,22 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Properties; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; import org.apache.maven.api.Artifact; import org.apache.maven.api.LocalRepository; import org.apache.maven.api.Project; import org.apache.maven.api.RemoteRepository; import org.apache.maven.api.Session; +import org.apache.maven.api.SessionData; import org.apache.maven.api.model.Model; import org.apache.maven.api.model.Repository; import org.apache.maven.api.services.ArtifactDeployer; @@ -48,9 +53,14 @@ import org.apache.maven.api.services.ProjectBuilderResult; import org.apache.maven.api.services.ProjectManager; import org.apache.maven.api.services.RepositoryFactory; +import org.apache.maven.api.services.VersionParser; import org.apache.maven.api.services.xml.ModelXmlFactory; import org.apache.maven.internal.impl.DefaultModelXmlFactory; +import org.apache.maven.internal.impl.DefaultVersionParser; +import org.apache.maven.internal.impl.InternalSession; import org.apache.maven.model.v4.MavenStaxReader; +import org.apache.maven.repository.internal.DefaultModelVersionParser; +import org.eclipse.aether.util.version.GenericVersionScheme; import org.mockito.ArgumentMatchers; import static org.mockito.ArgumentMatchers.any; @@ -67,17 +77,27 @@ */ public class SessionStub { - public static Session getMockSession(String localRepo) { + public static InternalSession getMockSession(String localRepo) { LocalRepository localRepository = mock(LocalRepository.class); when(localRepository.getId()).thenReturn("local"); when(localRepository.getPath()).thenReturn(Paths.get(localRepo)); return getMockSession(localRepository); } - public static Session getMockSession(LocalRepository localRepository) { - Session session = mock(Session.class); + public static InternalSession getMockSession(LocalRepository localRepository) { + InternalSession session = mock(InternalSession.class); + // + // RepositoryFactory + // RepositoryFactory repositoryFactory = mock(RepositoryFactory.class); + when(session.createRemoteRepository(anyString(), anyString())).thenAnswer(iom -> { + String id = iom.getArgument(0, String.class); + String url = iom.getArgument(1, String.class); + return session.getService(RepositoryFactory.class).createRemote(id, url); + }); + when(session.createRemoteRepository(any())) + .thenAnswer(iom -> repositoryFactory.createRemote(iom.getArgument(0, Repository.class))); when(repositoryFactory.createRemote(any(Repository.class))).thenAnswer(iom -> { Repository repository = iom.getArgument(0, Repository.class); return repositoryFactory.createRemote(repository.getId(), repository.getUrl()); @@ -92,16 +112,56 @@ public static Session getMockSession(LocalRepository localRepository) { when(remoteRepository.getProtocol()).thenReturn(URI.create(url).getScheme()); return remoteRepository; }); + when(session.getService(RepositoryFactory.class)).thenReturn(repositoryFactory); + + // + // VersionParser + // + VersionParser versionParser = + new DefaultVersionParser(new DefaultModelVersionParser(new GenericVersionScheme())); + when(session.parseVersion(any())) + .thenAnswer(iom -> versionParser.parseVersion(iom.getArgument(0, String.class))); + when(session.getService(VersionParser.class)).thenReturn(versionParser); + // + // LocalRepositoryManager + // LocalRepositoryManager localRepositoryManager = mock(LocalRepositoryManager.class); + when(session.getPathForLocalArtifact(any(Artifact.class))) + .then(iom -> localRepositoryManager.getPathForLocalArtifact( + session, session.getLocalRepository(), iom.getArgument(0, Artifact.class))); + when(session.getPathForRemoteArtifact(any(), any())) + .thenAnswer(iom -> localRepositoryManager.getPathForRemoteArtifact( + session, + session.getLocalRepository(), + iom.getArgument(0, RemoteRepository.class), + iom.getArgument(1, Artifact.class))); when(localRepositoryManager.getPathForLocalArtifact(any(), any(), any())) .thenAnswer(iom -> { LocalRepository localRepo = iom.getArgument(1, LocalRepository.class); Artifact artifact = iom.getArgument(2, Artifact.class); return localRepo.getPath().resolve(getPathForArtifact(artifact, true)); }); + when(session.getService(LocalRepositoryManager.class)).thenReturn(localRepositoryManager); + // + // ArtifactInstaller + // ArtifactInstaller artifactInstaller = mock(ArtifactInstaller.class); + doAnswer(iom -> { + artifactInstaller.install( + ArtifactInstallerRequest.build(session, iom.getArgument(0, Collection.class))); + return null; + }) + .when(session) + .installArtifacts(any(Collection.class)); + doAnswer(iom -> { + artifactInstaller.install(ArtifactInstallerRequest.build( + session, Arrays.asList(iom.getArgument(0, Artifact[].class)))); + return null; + }) + .when(session) + .installArtifacts(any(Artifact[].class)); doAnswer(iom -> { artifactInstaller.install(ArtifactInstallerRequest.build( iom.getArgument(0, Session.class), iom.getArgument(1, Collection.class))); @@ -109,8 +169,21 @@ public static Session getMockSession(LocalRepository localRepository) { }) .when(artifactInstaller) .install(any(Session.class), ArgumentMatchers.>any()); + when(session.getService(ArtifactInstaller.class)).thenReturn(artifactInstaller); + // + // ArtifactDeployer + // ArtifactDeployer artifactDeployer = mock(ArtifactDeployer.class); + doAnswer(iom -> { + artifactDeployer.deploy(ArtifactDeployerRequest.build( + iom.getArgument(0, Session.class), + iom.getArgument(1, RemoteRepository.class), + Arrays.asList(iom.getArgument(2, Artifact[].class)))); + return null; + }) + .when(session) + .deployArtifact(any(), any()); doAnswer(iom -> { artifactDeployer.deploy(ArtifactDeployerRequest.build( iom.getArgument(0, Session.class), @@ -120,7 +193,11 @@ public static Session getMockSession(LocalRepository localRepository) { }) .when(artifactDeployer) .deploy(any(), any(), any()); + when(session.getService(ArtifactDeployer.class)).thenReturn(artifactDeployer); + // + // ArtifactManager + // ArtifactManager artifactManager = mock(ArtifactManager.class); Map paths = new HashMap<>(); doAnswer(iom -> { @@ -132,7 +209,14 @@ public static Session getMockSession(LocalRepository localRepository) { doAnswer(iom -> Optional.ofNullable(paths.get(iom.getArgument(0, Artifact.class)))) .when(artifactManager) .getPath(any()); + doAnswer(iom -> artifactManager.getPath(iom.getArgument(0, Artifact.class))) + .when(session) + .getArtifactPath(any()); + when(session.getService(ArtifactManager.class)).thenReturn(artifactManager); + // + // ProjectManager + // ProjectManager projectManager = mock(ProjectManager.class); Map> attachedArtifacts = new HashMap<>(); doAnswer(iom -> { @@ -164,7 +248,18 @@ public static Session getMockSession(LocalRepository localRepository) { when(projectManager.getAttachedArtifacts(any())) .then(iom -> attachedArtifacts.computeIfAbsent(iom.getArgument(0, Project.class), p -> new ArrayList<>())); + when(projectManager.getAllArtifacts(any())).then(iom -> { + Project project = iom.getArgument(0, Project.class); + List result = new ArrayList<>(); + result.addAll(project.getArtifacts()); + result.addAll(attachedArtifacts.computeIfAbsent(project, p -> new ArrayList<>())); + return result; + }); + when(session.getService(ProjectManager.class)).thenReturn(projectManager); + // + // ArtifactFactory + // ArtifactFactory artifactFactory = mock(ArtifactFactory.class); when(artifactFactory.create(any())).then(iom -> { ArtifactFactoryRequest request = iom.getArgument(0, ArtifactFactoryRequest.class); @@ -180,40 +275,6 @@ public static Session getMockSession(LocalRepository localRepository) { return new ArtifactStub( request.getGroupId(), request.getArtifactId(), classifier, request.getVersion(), extension); }); - - ProjectBuilder projectBuilder = mock(ProjectBuilder.class); - when(projectBuilder.build(any(ProjectBuilderRequest.class))).then(iom -> { - ProjectBuilderRequest request = iom.getArgument(0, ProjectBuilderRequest.class); - ProjectBuilderResult result = mock(ProjectBuilderResult.class); - Model model = new MavenStaxReader().read(request.getSource().get().openStream()); - ProjectStub projectStub = new ProjectStub(); - projectStub.setModel(model); - ArtifactStub artifactStub = new ArtifactStub( - model.getGroupId(), model.getArtifactId(), "", model.getVersion(), model.getPackaging()); - projectStub.setArtifact(artifactStub); - when(result.getProject()).thenReturn(Optional.of(projectStub)); - return result; - }); - - Properties sysProps = new Properties(); - Properties usrProps = new Properties(); - doReturn(sysProps).when(session).getSystemProperties(); - doReturn(usrProps).when(session).getUserProperties(); - - when(session.getLocalRepository()).thenReturn(localRepository); - when(session.getService(RepositoryFactory.class)).thenReturn(repositoryFactory); - when(session.getService(ProjectBuilder.class)).thenReturn(projectBuilder); - when(session.getService(LocalRepositoryManager.class)).thenReturn(localRepositoryManager); - when(session.getService(ProjectManager.class)).thenReturn(projectManager); - when(session.getService(ArtifactManager.class)).thenReturn(artifactManager); - when(session.getService(ArtifactInstaller.class)).thenReturn(artifactInstaller); - when(session.getService(ArtifactDeployer.class)).thenReturn(artifactDeployer); - when(session.getService(ArtifactFactory.class)).thenReturn(artifactFactory); - when(session.getService(ModelXmlFactory.class)).thenReturn(new DefaultModelXmlFactory()); - - when(session.getPathForLocalArtifact(any(Artifact.class))) - .then(iom -> localRepositoryManager.getPathForLocalArtifact( - session, session.getLocalRepository(), iom.getArgument(0, Artifact.class))); when(session.createArtifact(any(), any(), any(), any(), any(), any())).thenAnswer(iom -> { String groupId = iom.getArgument(0, String.class); String artifactId = iom.getArgument(1, String.class); @@ -246,17 +307,45 @@ public static Session getMockSession(LocalRepository localRepository) { .extension(extension) .build()); }); - when(session.createRemoteRepository(anyString(), anyString())).thenAnswer(iom -> { - String id = iom.getArgument(0, String.class); - String url = iom.getArgument(1, String.class); - return session.getService(RepositoryFactory.class).createRemote(id, url); + when(session.getService(ArtifactFactory.class)).thenReturn(artifactFactory); + + // + // ProjectBuilder + // + ProjectBuilder projectBuilder = mock(ProjectBuilder.class); + when(projectBuilder.build(any(ProjectBuilderRequest.class))).then(iom -> { + ProjectBuilderRequest request = iom.getArgument(0, ProjectBuilderRequest.class); + ProjectBuilderResult result = mock(ProjectBuilderResult.class); + Model model = new MavenStaxReader().read(request.getSource().get().openStream()); + ProjectStub projectStub = new ProjectStub(); + projectStub.setModel(model); + ArtifactStub artifactStub = new ArtifactStub( + model.getGroupId(), model.getArtifactId(), "", model.getVersion(), model.getPackaging()); + if (!"pom".equals(model.getPackaging())) { + projectStub.setMainArtifact(artifactStub); + } + when(result.getProject()).thenReturn(Optional.of(projectStub)); + return result; }); - doAnswer(iom -> artifactManager.getPath(iom.getArgument(0, Artifact.class))) - .when(session) - .getArtifactPath(any()); + when(session.getService(ProjectBuilder.class)).thenReturn(projectBuilder); + + // + // ModelXmlFactory + // + when(session.getService(ModelXmlFactory.class)).thenReturn(new DefaultModelXmlFactory()); + // + // Other + // + Properties sysProps = new Properties(); + Properties usrProps = new Properties(); + doReturn(sysProps).when(session).getSystemProperties(); + doReturn(usrProps).when(session).getUserProperties(); + when(session.getLocalRepository()).thenReturn(localRepository); + when(session.getData()).thenReturn(new TestSessionData()); when(session.withLocalRepository(any())) .thenAnswer(iom -> getMockSession(iom.getArgument(0, LocalRepository.class))); + return session; } @@ -275,4 +364,30 @@ static String getPathForArtifact(Artifact artifact, boolean local) { } return path.toString(); } + + static class TestSessionData implements SessionData { + private final Map, Object> map = new ConcurrentHashMap<>(); + + @Override + public void set(Key key, T value) { + map.put(key, value); + } + + @Override + public boolean replace(Key key, T oldValue, T newValue) { + return map.replace(key, oldValue, newValue); + } + + @Override + @SuppressWarnings("unchecked") + public T get(Key key) { + return (T) map.get(key); + } + + @Override + @SuppressWarnings("unchecked") + public T computeIfAbsent(Key key, Supplier supplier) { + return (T) map.computeIfAbsent(key, k -> supplier.get()); + } + } } diff --git a/maven-plugin-testing-harness/src/test/java/org/apache/maven/api/di/testing/SimpleDITest.java b/maven-plugin-testing-harness/src/test/java/org/apache/maven/api/di/testing/SimpleDITest.java new file mode 100644 index 0000000..429a3f5 --- /dev/null +++ b/maven-plugin-testing-harness/src/test/java/org/apache/maven/api/di/testing/SimpleDITest.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.maven.api.di.testing; + +import java.io.File; + +import org.apache.maven.api.Session; +import org.apache.maven.api.di.Inject; +import org.apache.maven.api.di.Provides; +import org.apache.maven.api.plugin.testing.stubs.SessionStub; +import org.junit.jupiter.api.Test; + +import static org.apache.maven.api.di.testing.MavenDIExtension.getBasedir; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +@MavenDITest +public class SimpleDITest { + + private static final String LOCAL_REPO = getBasedir() + File.separator + "target" + File.separator + "local-repo"; + + @Inject + Session session; + + @Test + void testSession() { + assertNotNull(session); + assertNotNull(session.getLocalRepository()); + } + + @Provides + Session createSession() { + return SessionStub.getMockSession(LOCAL_REPO); + } +} diff --git a/maven-plugin-testing-harness/src/test/java/org/apache/maven/api/plugin/testing/ExpressionEvaluatorTest.java b/maven-plugin-testing-harness/src/test/java/org/apache/maven/api/plugin/testing/ExpressionEvaluatorTest.java index b287bbf..51b1ed9 100644 --- a/maven-plugin-testing-harness/src/test/java/org/apache/maven/api/plugin/testing/ExpressionEvaluatorTest.java +++ b/maven-plugin-testing-harness/src/test/java/org/apache/maven/api/plugin/testing/ExpressionEvaluatorTest.java @@ -18,14 +18,17 @@ */ package org.apache.maven.api.plugin.testing; -import javax.inject.Named; - +import java.nio.file.Path; import java.nio.file.Paths; import java.util.Properties; -import com.google.inject.Provides; +import org.apache.maven.api.Project; import org.apache.maven.api.Session; +import org.apache.maven.api.di.Named; +import org.apache.maven.api.di.Provides; import org.apache.maven.api.plugin.MojoException; +import org.apache.maven.api.plugin.annotations.Mojo; +import org.apache.maven.api.plugin.testing.stubs.ProjectStub; import org.apache.maven.api.plugin.testing.stubs.SessionStub; import org.junit.jupiter.api.Test; @@ -42,16 +45,18 @@ public class ExpressionEvaluatorTest { private static final String LOCAL_REPO = "target/local-repo/"; - private static final String ARTIFACT_ID = "maven-test-mojo"; - private static final String COORDINATES = "groupId:" + ARTIFACT_ID + ":version:goal"; + private static final String GROUP_ID = "test"; + private static final String ARTIFACT_ID = "test-plugin"; + private static final String COORDINATES = GROUP_ID + ":" + ARTIFACT_ID + ":0.0.1-SNAPSHOT:goal"; private static final String CONFIG = "\n" + " \n" + " \n" + " \n" + + " " + GROUP_ID + "\n" + " " + ARTIFACT_ID + "\n" + " \n" - + " ${basedir}\n" - + " ${basedir}/workDirectory\n" + + " ${project.basedir}\n" + + " ${project.basedir}/workDirectory\n" + " \n" + " \n" + " \n" @@ -68,6 +73,7 @@ public void testInjection(ExpressionEvaluatorMojo mojo) { @Test @InjectMojo(goal = COORDINATES, pom = CONFIG) + @Basedir("${basedir}/target/test-classes") @MojoParameter(name = "param", value = "paramValue") public void testParam(ExpressionEvaluatorMojo mojo) { assertNotNull(mojo.basedir); @@ -88,11 +94,12 @@ public void testParams(ExpressionEvaluatorMojo mojo) { assertDoesNotThrow(mojo::execute); } - @Named(COORDINATES) + @Mojo(name = "goal") + @Named("test:test-plugin:0.0.1-SNAPSHOT:goal") // this one is usually generated by maven-plugin-plugin public static class ExpressionEvaluatorMojo implements org.apache.maven.api.plugin.Mojo { - private String basedir; + private Path basedir; - private String workdir; + private Path workdir; private String param; @@ -101,11 +108,11 @@ public static class ExpressionEvaluatorMojo implements org.apache.maven.api.plug /** {@inheritDoc} */ @Override public void execute() throws MojoException { - if (basedir == null || basedir.isEmpty()) { + if (basedir == null) { throw new MojoException("basedir was not injected."); } - if (workdir == null || workdir.isEmpty()) { + if (workdir == null) { throw new MojoException("workdir was not injected."); } else if (!workdir.startsWith(basedir)) { throw new MojoException("workdir does not start with basedir."); @@ -122,4 +129,11 @@ Session session() { doAnswer(iom -> Paths.get(MojoExtension.getBasedir())).when(session).getRootDirectory(); return session; } + + @Provides + Project project() { + ProjectStub project = new ProjectStub(); + project.setBasedir(Paths.get(MojoExtension.getBasedir())); + return project; + } } diff --git a/maven-plugin-testing-harness/src/test/resources/META-INF/maven/org.apache.maven.api.di.Inject b/maven-plugin-testing-harness/src/test/resources/META-INF/maven/org.apache.maven.api.di.Inject new file mode 100644 index 0000000..2a771d7 --- /dev/null +++ b/maven-plugin-testing-harness/src/test/resources/META-INF/maven/org.apache.maven.api.di.Inject @@ -0,0 +1,19 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +org.apache.maven.api.plugin.testing.ExpressionEvaluatorTest$ExpressionEvaluatorMojo \ No newline at end of file diff --git a/pom.xml b/pom.xml index 4cea8e8..6d530ae 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ under the License. org.apache.maven maven-parent - 40 + 42 @@ -65,9 +65,9 @@ under the License. 3.2.1 - 4.0.0-alpha-8 + 4.0.0-beta-3 plugin-testing-archives/LATEST - 8 + 17 2023-11-07T21:58:12Z