Skip to content

Commit c930d72

Browse files
Support RepositorySession to interact with remote repositories
- update documentation
1 parent 77f1161 commit c930d72

8 files changed

Lines changed: 331 additions & 113 deletions

File tree

maven-plugin-testing-harness/pom.xml

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ under the License.
3131
<description>The Maven Plugin Testing Harness provides mechanisms to manage tests on Mojo.</description>
3232

3333
<properties>
34-
<versions.junit5>5.13.0</versions.junit5>
34+
<mavenVersion>3.9.12</mavenVersion>
35+
<resloverVersion>1.9.25</resloverVersion>
3536
<wagonVersion>3.5.3</wagonVersion>
3637
</properties>
3738

@@ -61,6 +62,12 @@ under the License.
6162
<version>${mavenVersion}</version>
6263
<scope>provided</scope>
6364
</dependency>
65+
<dependency>
66+
<groupId>org.apache.maven.plugin-tools</groupId>
67+
<artifactId>maven-plugin-annotations</artifactId>
68+
<version>${version.maven-plugin-tools}</version>
69+
<scope>test</scope>
70+
</dependency>
6471
<dependency>
6572
<groupId>javax.inject</groupId>
6673
<artifactId>javax.inject</artifactId>
@@ -110,6 +117,27 @@ under the License.
110117
<optional>true</optional>
111118
</dependency>
112119

120+
<!-- START SNIPPET: resolver-transport -->
121+
<dependency>
122+
<groupId>org.apache.maven.resolver</groupId>
123+
<artifactId>maven-resolver-connector-basic</artifactId>
124+
<version>${resloverVersion}</version>
125+
<scope>test</scope>
126+
</dependency>
127+
<dependency>
128+
<groupId>org.apache.maven.resolver</groupId>
129+
<artifactId>maven-resolver-transport-file</artifactId>
130+
<version>${resloverVersion}</version>
131+
<scope>test</scope>
132+
</dependency>
133+
<dependency>
134+
<groupId>org.apache.maven.resolver</groupId>
135+
<artifactId>maven-resolver-transport-http</artifactId>
136+
<version>${resloverVersion}</version>
137+
<scope>test</scope>
138+
</dependency>
139+
<!-- END SNIPPET: resolver-transport -->
140+
113141
<dependency>
114142
<groupId>org.codehaus.plexus</groupId>
115143
<artifactId>plexus-testing</artifactId>

maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/MojoExtension.java

Lines changed: 116 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import java.util.Arrays;
3636
import java.util.Collection;
3737
import java.util.Collections;
38+
import java.util.Date;
3839
import java.util.HashMap;
3940
import java.util.LinkedHashSet;
4041
import java.util.List;
@@ -50,9 +51,14 @@
5051
import com.google.inject.Binder;
5152
import com.google.inject.Module;
5253
import com.google.inject.internal.ProviderMethodsModule;
54+
import org.apache.maven.RepositoryUtils;
5355
import org.apache.maven.api.di.Provides;
56+
import org.apache.maven.execution.DefaultMavenExecutionRequest;
57+
import org.apache.maven.execution.MavenExecutionRequest;
58+
import org.apache.maven.execution.MavenExecutionRequestPopulator;
5459
import org.apache.maven.execution.MavenSession;
5560
import org.apache.maven.execution.scope.internal.MojoExecutionScope;
61+
import org.apache.maven.internal.aether.DefaultRepositorySystemSessionFactory;
5662
import org.apache.maven.lifecycle.internal.MojoDescriptorCreator;
5763
import org.apache.maven.plugin.Mojo;
5864
import org.apache.maven.plugin.MojoExecution;
@@ -81,6 +87,7 @@
8187
import org.codehaus.plexus.util.xml.XmlStreamReader;
8288
import org.codehaus.plexus.util.xml.Xpp3Dom;
8389
import org.codehaus.plexus.util.xml.Xpp3DomBuilder;
90+
import org.eclipse.aether.RepositorySystemSession;
8491
import org.junit.jupiter.api.extension.ExtensionContext;
8592
import org.junit.jupiter.api.extension.ParameterContext;
8693
import org.junit.jupiter.api.extension.ParameterResolutionException;
@@ -93,6 +100,7 @@
93100
import static org.mockito.Mockito.clearInvocations;
94101
import static org.mockito.Mockito.lenient;
95102
import static org.mockito.Mockito.mockingDetails;
103+
import static org.mockito.Mockito.spy;
96104

97105
/**
98106
* JUnit Jupiter extension that provides support for testing Maven plugins (Mojos).
@@ -223,6 +231,9 @@ public void beforeEach(ExtensionContext context) throws Exception {
223231
MojoExecution mojoExecution = addMock(plexusContainer, MojoExecution.class, this::mockMojoExecution);
224232
MavenSession mavenSession = addMock(plexusContainer, MavenSession.class, this::mockMavenSession);
225233

234+
// prepare MavenExecutionRequest to be available in BeforeEach methods in test classes
235+
createMavenExecutionRequest(context);
236+
226237
SessionScope sessionScope = plexusContainer.lookup(SessionScope.class);
227238
sessionScope.enter();
228239
sessionScope.seed(MavenSession.class, mavenSession);
@@ -388,6 +399,11 @@ protected Mojo lookupMojo(
388399
ExtensionContext extensionContext, String[] coord, Xpp3Dom pluginConfiguration, PluginDescriptor descriptor)
389400
throws Exception {
390401
PlexusContainer plexusContainer = getContainer(extensionContext);
402+
403+
MavenExecutionRequest request = setupMavenExecutionRequest(extensionContext);
404+
plexusContainer.lookup(MavenExecutionRequestPopulator.class).populateDefaults(request);
405+
setupRepositorySession(extensionContext, request);
406+
391407
// pluginkey = groupId : artifactId : version : goal
392408
Mojo mojo = plexusContainer.lookup(Mojo.class, coord[0] + ":" + coord[1] + ":" + coord[2] + ":" + coord[3]);
393409

@@ -405,15 +421,18 @@ protected Mojo lookupMojo(
405421
MojoExecution mojoExecution = plexusContainer.lookup(MojoExecution.class);
406422

407423
if (mockingDetails(session).isMock()) {
408-
lenient().when(session.getCurrentProject()).thenReturn(mavenProject);
424+
lenient().doReturn(mavenProject).when(session).getCurrentProject();
409425
}
410426

411427
if (mockingDetails(mavenProject).isMock()) {
412-
lenient().when(mavenProject.getBasedir()).thenReturn(new File(getTestBasedir(extensionContext)));
428+
lenient()
429+
.doReturn(new File(getTestBasedir(extensionContext)))
430+
.when(mavenProject)
431+
.getBasedir();
413432
}
414433

415434
if (mojoDescriptor.isPresent() && mockingDetails(mojoExecution).isMock()) {
416-
lenient().when(mojoExecution.getMojoDescriptor()).thenReturn(mojoDescriptor.get());
435+
lenient().doReturn(mojoDescriptor.get()).when(mojoExecution).getMojoDescriptor();
417436
}
418437

419438
if (pluginConfiguration != null) {
@@ -445,6 +464,100 @@ protected Mojo lookupMojo(
445464
return mojo;
446465
}
447466

467+
private boolean isRealRepositorySessionNotRequired(ExtensionContext context) {
468+
return !AnnotationSupport.findAnnotation(context.getTestClass(), MojoTest.class)
469+
.map(MojoTest::realRepositorySession)
470+
.orElse(false);
471+
}
472+
473+
/**
474+
* Create a MavenExecutionRequest if not already present in the MavenSession
475+
*/
476+
private void createMavenExecutionRequest(ExtensionContext context) throws ComponentLookupException {
477+
PlexusContainer container = getContainer(context);
478+
MavenSession session = container.lookup(MavenSession.class);
479+
MavenExecutionRequest request = session.getRequest();
480+
481+
if (request == null && mockingDetails(session).isMock()) {
482+
lenient()
483+
.doReturn(spy(new DefaultMavenExecutionRequest()))
484+
.when(session)
485+
.getRequest();
486+
}
487+
}
488+
489+
private MavenExecutionRequest setupMavenExecutionRequest(ExtensionContext context) throws ComponentLookupException {
490+
PlexusContainer container = getContainer(context);
491+
MavenSession session = container.lookup(MavenSession.class);
492+
MavenExecutionRequest request = session.getRequest();
493+
494+
if (request == null) {
495+
// user can provide own MavenSession instance without a request
496+
request = new DefaultMavenExecutionRequest();
497+
}
498+
499+
if (request.getStartTime() == null) {
500+
request.setStartTime(new Date());
501+
}
502+
503+
if (request.getUserProperties().isEmpty()) {
504+
request.setUserProperties(session.getUserProperties());
505+
}
506+
507+
if (request.getSystemProperties().isEmpty()) {
508+
request.setSystemProperties(session.getSystemProperties());
509+
}
510+
511+
// set a default local repository path if none is set
512+
if (request.getLocalRepositoryPath() == null && request.getLocalRepository() == null) {
513+
request.setLocalRepositoryPath(getTestBasedir(context) + "/target/local-repo");
514+
}
515+
516+
if (request.getBaseDirectory() == null) {
517+
request.setBaseDirectory(new File(getTestBasedir(context)));
518+
}
519+
520+
return request;
521+
}
522+
523+
private void setupRepositorySession(ExtensionContext context, MavenExecutionRequest request)
524+
throws ComponentLookupException {
525+
526+
if (isRealRepositorySessionNotRequired(context)) {
527+
return;
528+
}
529+
530+
PlexusContainer container = getContainer(context);
531+
532+
MavenProject mavenProject = container.lookup(MavenProject.class);
533+
if (mockingDetails(mavenProject).isMock()) {
534+
lenient()
535+
.doReturn(request.getRemoteRepositories())
536+
.when(mavenProject)
537+
.getRemoteArtifactRepositories();
538+
lenient()
539+
.doReturn(request.getPluginArtifactRepositories())
540+
.when(mavenProject)
541+
.getPluginArtifactRepositories();
542+
lenient()
543+
.doReturn(RepositoryUtils.toRepos(request.getRemoteRepositories()))
544+
.when(mavenProject)
545+
.getRemoteProjectRepositories();
546+
lenient()
547+
.doReturn(RepositoryUtils.toRepos(request.getPluginArtifactRepositories()))
548+
.when(mavenProject)
549+
.getRemotePluginRepositories();
550+
}
551+
552+
RepositorySystemSession repositorySystemSession =
553+
container.lookup(DefaultRepositorySystemSessionFactory.class).newRepositorySession(request);
554+
555+
MavenSession session = container.lookup(MavenSession.class);
556+
if (mockingDetails(session).isMock()) {
557+
lenient().doReturn(repositorySystemSession).when(session).getRepositorySession();
558+
}
559+
}
560+
448561
private Xpp3Dom finalizeConfig(Xpp3Dom config, MojoDescriptor mojoDescriptor) {
449562
List<Xpp3Dom> children = new ArrayList<>();
450563
if (mojoDescriptor != null && mojoDescriptor.getParameters() != null) {

maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/MojoTest.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,4 +83,12 @@
8383
@Retention(RetentionPolicy.RUNTIME)
8484
@ExtendWith(MojoExtension.class)
8585
@Target(ElementType.TYPE)
86-
public @interface MojoTest {}
86+
public @interface MojoTest {
87+
/**
88+
* Indicates whether to use a real repository session for the test.
89+
* <br>
90+
* When set to {@code true}, the test will utilize a real repository session,
91+
* allowing for artifact resolution and repository interactions.
92+
*/
93+
boolean realRepositorySession() default false;
94+
}

maven-plugin-testing-harness/src/site/markdown/examples/repositories.md

Lines changed: 16 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -18,129 +18,38 @@ date: February 2008
1818
<!-- KIND, either express or implied. See the License for the -->
1919
<!-- specific language governing permissions and limitations -->
2020
<!-- under the License. -->
21-
## Testing Using Repositories
22-
23-
### NOTE
24-
25-
`JUnit 3` based tests are deprecated since `3.4.0`.
26-
27-
Use JUnit 5 annotations, consult [javadocs](../apidocs/org/apache/maven/api/plugin/testing/package-summary.html) for examples.
28-
29-
**Note**: This example improves the [cookbook](../getting-started/index.html) for testing repositories.
30-
31-
32-
When developing a Maven plugin you often need to play with repositories. Suppose that the MyMojo needs to download artifacts into your local repository, i.e.:
33-
34-
3521

36-
```
37-
public class MyMojo
38-
extends AbstractMojo
39-
{
40-
/**
41-
* Used for resolving artifacts
42-
*/
43-
@Component
44-
private ArtifactResolver resolver;
45-
46-
/**
47-
* Factory for creating artifact objects
48-
*/
49-
@Component
50-
private ArtifactFactory factory;
51-
52-
/**
53-
* Local Repository.
54-
*/
55-
@Parameter( defaultValue = "${localRepository}", readonly = true, required = true )
56-
private ArtifactRepository localRepository;
57-
58-
public void execute()
59-
throws MojoExecutionException
60-
{
61-
...
62-
63-
Artifact artifact = factory.createArtifact( "junit", "junit", "3.8.1", "compile", "jar" );
64-
try
65-
{
66-
resolver.resolve( artifact, project.getRemoteArtifactRepositories(), localRepository );
67-
}
68-
catch ( ArtifactResolutionException e )
69-
{
70-
throw new MojoExecutionException( "Unable to resolve artifact:" + artifact, e );
71-
}
72-
catch ( ArtifactNotFoundException e )
73-
{
74-
throw new MojoExecutionException( "Unable to find artifact:" + artifact, e );
75-
}
76-
77-
...
78-
}
79-
}
80-
```
81-
82-
### Create Stubs
22+
## Testing Using Repositories
8323

24+
**Note**: This example improves the [cookbook](../getting-started/index.html) for testing repositories.
25+
26+
When developing a Maven plugin you often need to play with repositories.
27+
Suppose that the Mojo needs to download artifacts into your local repository.
8428

85-
Stub for the test project:
29+
You need annotate unit test with `@MojoTest(realRepositorySession = true)` to enable real repository session.
8630

31+
Then provided mock for `MavenSession` will have a real repository session with local repository configured.
8732

33+
Mock for `MavenProject` will also have mocked methods `getRemote*Repositories`.
8834

89-
```
90-
public class MyProjectStub
91-
extends MavenProjectStub
92-
{
93-
/**
94-
* Default constructor
95-
*/
96-
public MyProjectStub()
97-
{
98-
...
99-
}
35+
### Project dependencies for test
10036

101-
/** {@inheritDoc} */
102-
public List getRemoteArtifactRepositories()
103-
{
104-
ArtifactRepository repository = new DefaultArtifactRepository( "central", "http://repo.maven.apache.org/maven2",
105-
new DefaultRepositoryLayout() );
37+
For real repository session you need to add resolver transport dependency in test scope to your `pom.xml`:
10638

107-
return Collections.singletonList( repository );
108-
}
109-
}
110-
```
39+
<!-- MACRO{snippet|id=resolver-transport|file=maven-plugin-testing-harness/pom.xml} -->
11140

41+
### Example Mojo to test
11242

113-
### Configure `project-to-test` pom
43+
<!-- MACRO{snippet|id=resolve-mojo|file=maven-plugin-testing-harness/src/test/java/org/apache/maven/plugin/testing/SimpleResolveMojo.java} -->
11444

45+
### Unit test
11546

47+
<!-- MACRO{snippet|id=resolve-mojo-test|file=maven-plugin-testing-harness/src/test/java/org/apache/maven/plugin/testing/SimpleResolveMojoTest.java} -->
11648

117-
```
118-
<project>
119-
...
120-
<build>
121-
<plugins>
122-
<plugin>
123-
<artifactId>maven-my-plugin</artifactId>
124-
<configuration>
125-
<!-- Specify where this pom will output files -->
126-
<outputDirectory>${basedir}/target/test-harness/project-to-test</outputDirectory>
127-
128-
<!-- By default <<<${basedir}/target/local-repo", where basedir refers
129-
to the basedir of maven-my-plugin. -->
130-
<localRepository>${localRepository}</localRepository>
131-
<!-- The defined stub -->
132-
<project implementation="org.apache.maven.plugin.my.stubs.MyProjectStub"/>
133-
</configuration>
134-
</plugin>
135-
</plugins>
136-
</build>
137-
</project>
138-
```
13949

14050
#### Execute test
14151

142-
143-
Calling `mvn test` will create `$\{basedir\}/target/local-repo/junitjunit/3.8.1/junit-3.8.1.jar` file.
52+
Calling `mvn test` will create `<temp-directory>/org/apache/commons/commons-lang3/3.20.0/commons-lang3-3.20.0.jar` file.
14453

14554

14655

0 commit comments

Comments
 (0)