Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -119,15 +119,55 @@ public void attachArtifact(@Nonnull Project project, @Nonnull ProducedArtifact a
artifact.getExtension(),
null);
}
if (!Objects.equals(project.getGroupId(), artifact.getGroupId())
|| !Objects.equals(project.getArtifactId(), artifact.getArtifactId())
|| !Objects.equals(
project.getVersion(), artifact.getBaseVersion().toString())) {
throw new IllegalArgumentException(
"The produced artifact must have the same groupId/artifactId/version than the project it is attached to. Expecting "
+ project.getGroupId() + ":" + project.getArtifactId() + ":" + project.getVersion()
+ " but received " + artifact.getGroupId() + ":" + artifact.getArtifactId() + ":"
+ artifact.getBaseVersion());
// Verify groupId and version, intentionally allow artifactId to differ as Maven project may be
// multi-module with modular sources structure that provide module names used as artifactIds.
String g1 = project.getGroupId();
String a1 = project.getArtifactId();
String v1 = project.getVersion();
String g2 = artifact.getGroupId();
String a2 = artifact.getArtifactId();
String v2 = artifact.getBaseVersion().toString();

// ArtifactId may differ only for multi-module projects, in which case
// it must match the module name from a source root in modular sources.
boolean isMultiModule = false;
boolean validArtifactId = Objects.equals(a1, a2);
for (SourceRoot sr : getSourceRoots(project)) {
Optional<String> moduleName = sr.module();
if (moduleName.isPresent()) {
isMultiModule = true;
if (moduleName.get().equals(a2)) {
validArtifactId = true;
break;
}
}
}
boolean isSameGroupAndVersion = Objects.equals(g1, g2) && Objects.equals(v1, v2);
if (!(isSameGroupAndVersion && validArtifactId)) {
String message;
if (isMultiModule) {
// Multi-module project: artifactId may match any declared module name
message = String.format(
"Cannot attach artifact to project: groupId and version must match the project, "
+ "and artifactId must match either the project or a declared module name.%n"
+ " Project coordinates: %s:%s:%s%n"
+ " Artifact coordinates: %s:%s:%s%n",
g1, a1, v1, g2, a2, v2);
if (isSameGroupAndVersion) {
message += String.format(
" Hint: The artifactId '%s' does not match the project artifactId '%s' "
+ "nor any declared module name in source roots.",
a2, a1);
}
} else {
// Non-modular project: artifactId must match exactly
message = String.format(
"Cannot attach artifact to project: groupId, artifactId and version must match the project.%n"
+ " Project coordinates: %s:%s:%s%n"
+ " Artifact coordinates: %s:%s:%s",
g1, a1, v1, g2, a2, v2);
}
throw new IllegalArgumentException(message);
}
getMavenProject(project)
.addAttachedArtifact(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,43 +20,102 @@

import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.function.Supplier;

import org.apache.maven.api.Language;
import org.apache.maven.api.ProducedArtifact;
import org.apache.maven.api.Project;
import org.apache.maven.api.ProjectScope;
import org.apache.maven.api.services.ArtifactManager;
import org.apache.maven.impl.DefaultModelVersionParser;
import org.apache.maven.impl.DefaultSourceRoot;
import org.apache.maven.impl.DefaultVersionParser;
import org.apache.maven.project.MavenProject;
import org.eclipse.aether.util.version.GenericVersionScheme;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.when;

class DefaultProjectManagerTest {

private DefaultProjectManager projectManager;

private Project project;

private ProducedArtifact artifact;

private Path artifactPath;

@Test
void attachArtifact() {
InternalMavenSession session = Mockito.mock(InternalMavenSession.class);
ArtifactManager artifactManager = Mockito.mock(ArtifactManager.class);
MavenProject mavenProject = new MavenProject();
Project project = new DefaultProject(session, mavenProject);
ProducedArtifact artifact = Mockito.mock(ProducedArtifact.class);
Path path = Paths.get("");
project = new DefaultProject(session, mavenProject);
artifact = Mockito.mock(ProducedArtifact.class);
artifactPath = Paths.get("");
DefaultVersionParser versionParser =
new DefaultVersionParser(new DefaultModelVersionParser(new GenericVersionScheme()));
DefaultProjectManager projectManager = new DefaultProjectManager(session, artifactManager);
projectManager = new DefaultProjectManager(session, artifactManager);

mavenProject.setGroupId("myGroup");
mavenProject.setArtifactId("myArtifact");
mavenProject.setVersion("1.0-SNAPSHOT");
when(artifact.getGroupId()).thenReturn("myGroup");
when(artifact.getArtifactId()).thenReturn("myArtifact");
when(artifact.getBaseVersion()).thenReturn(versionParser.parseVersion("1.0-SNAPSHOT"));
projectManager.attachArtifact(project, artifact, path);
projectManager.attachArtifact(project, artifact, artifactPath);

// Verify that an exception is thrown when the artifactId differs
when(artifact.getArtifactId()).thenReturn("anotherArtifact");
assertThrows(IllegalArgumentException.class, () -> projectManager.attachArtifact(project, artifact, path));
assertExceptionMessageContains("myGroup:myArtifact:1.0-SNAPSHOT", "myGroup:anotherArtifact:1.0-SNAPSHOT");

// Add a Java module. It should relax the restriction on artifactId.
projectManager.addSourceRoot(
project,
new DefaultSourceRoot(
ProjectScope.MAIN,
Language.JAVA_FAMILY,
"org.foo.bar",
null,
Path.of("myProject"),
null,
null,
false,
null,
true));

// Verify that we get the same exception when the artifactId does not match the module name
assertExceptionMessageContains("", "anotherArtifact");

// Verify that no exception is thrown when the artifactId is the module name
when(artifact.getArtifactId()).thenReturn("org.foo.bar");
projectManager.attachArtifact(project, artifact, artifactPath);

// Verify that an exception is thrown when the groupId differs
when(artifact.getGroupId()).thenReturn("anotherGroup");
assertExceptionMessageContains("myGroup:myArtifact:1.0-SNAPSHOT", "anotherGroup:org.foo.bar:1.0-SNAPSHOT");
}

/**
* Verifies that {@code projectManager.attachArtifact(…)} throws an exception,
* and that the expecption message contains the expected and actual <abbr>GAV</abbr>.
*
* @param expectedGAV the actual <abbr>GAV</abbr> that the exception message should contain
* @param actualGAV the actual <abbr>GAV</abbr> that the exception message should contain
*/
private void assertExceptionMessageContains(String expectedGAV, String actualGAV) {
String cause = assertThrows(
IllegalArgumentException.class,
() -> projectManager.attachArtifact(project, artifact, artifactPath))
.getMessage();
Supplier<String> message = () ->
String.format("The exception message does not contain the expected GAV. Message was:%n%s%n", cause);

assertTrue(cause.contains(expectedGAV), message);
assertTrue(cause.contains(actualGAV), message);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public DefaultSourceRoot(
@Nonnull Language language,
@Nullable String moduleName,
@Nullable Version targetVersionOrNull,
@Nullable Path directory,
@Nonnull Path directory,
@Nullable List<String> includes,
@Nullable List<String> excludes,
boolean stringFiltering,
Expand Down