Skip to content

Commit

Permalink
Merge pull request #301 from aloubyansky/manifest-cyclic-dep-test
Browse files Browse the repository at this point in the history
Add a test for manifesting cyclic dependencies
  • Loading branch information
aloubyansky authored Nov 18, 2023
2 parents 18dd86e + ed3f14c commit 5444a86
Show file tree
Hide file tree
Showing 3 changed files with 208 additions and 12 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package io.quarkus.domino.manifest;

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

import io.quarkus.bootstrap.resolver.maven.MavenArtifactResolver;
import io.quarkus.domino.ProjectDependencyConfig;
import io.quarkus.domino.ProjectDependencyResolver;
import io.quarkus.domino.test.repo.TestArtifactRepo;
import io.quarkus.domino.test.repo.TestProject;
import io.quarkus.maven.dependency.ArtifactCoords;
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashSet;
import java.util.List;
import org.cyclonedx.exception.ParseException;
import org.cyclonedx.model.Bom;
import org.cyclonedx.model.Component;
import org.cyclonedx.model.Dependency;
import org.cyclonedx.parsers.JsonParser;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

public class CyclicDependencyGraphTest {

@TempDir
static Path testRepoDir;
static MavenArtifactResolver artifactResolver;

@BeforeAll
static void prepareRepo() {
var testRepo = TestArtifactRepo.of(testRepoDir);
artifactResolver = testRepo.getArtifactResolver();

var tcnativeProject = TestProject.of("io.netty", "1.0")
.setRepoUrl("https://netty.io/tcnative")
.setTag("1.0");
tcnativeProject.createMainModule("netty-tcnative-boringssl-static")
.addClassifier("linux-aarch_64")
.addDependency(ArtifactCoords.jar("io.netty", "netty-tcnative-boringssl-static", "linux-aarch_64", " 1.0"))
.addClassifier("linux-x86_64")
.addDependency(ArtifactCoords.jar("io.netty", "netty-tcnative-boringssl-static", "linux-x86_64", " 1.0"))
.addClassifier("osx-aarch_64")
.addDependency(ArtifactCoords.jar("io.netty", "netty-tcnative-boringssl-static", "osx-aarch_64", " 1.0"))
.addClassifier("osx-x86_64")
.addDependency(ArtifactCoords.jar("io.netty", "netty-tcnative-boringssl-static", "osx-x86_64", " 1.0"))
.addClassifier("windows-x86_64")
.addDependency(ArtifactCoords.jar("io.netty", "netty-tcnative-boringssl-static", "windows-x86_64", " 1.0"));
testRepo.install(tcnativeProject);

var appProject = TestProject.of("org.acme", "1.0")
.setRepoUrl("https://acme.org/app")
.setTag("1.0");
appProject.createMainModule("acme-app")
.addDependency(ArtifactCoords.jar("io.netty", "netty-tcnative-boringssl-static", "linux-x86_64", "1.0"));
testRepo.install(appProject);
}

private static ProjectDependencyConfig.Mutable newDependencyConfig() {
return ProjectDependencyConfig.builder()
.setWarnOnMissingScm(true)
.setLegacyScmLocator(true);
}

@Test
public void dependencyGraph() {
var config = newDependencyConfig()
.setProjectArtifacts(List.of(ArtifactCoords.jar("org.acme", "acme-app", "1.0")));

final Bom bom;
Path output = null;
try {
output = Files.createTempFile("domino-test", "sbom");

ProjectDependencyResolver.builder()
.setArtifactResolver(artifactResolver)
.setDependencyConfig(config)
.addDependencyTreeVisitor(new SbomGeneratingDependencyVisitor(
SbomGenerator.builder()
.setArtifactResolver(artifactResolver)
.setOutputFile(output)
.setEnableTransformers(false),
config))
.build()
.resolveDependencies();

try (BufferedReader reader = Files.newBufferedReader(output)) {
bom = new JsonParser().parse(reader);
} catch (ParseException e) {
throw new RuntimeException(e);
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (output != null) {
output.toFile().deleteOnExit();
}
}

assertDependencies(bom, ArtifactCoords.jar("org.acme", "acme-app", "1.0"),
ArtifactCoords.jar("io.netty", "netty-tcnative-boringssl-static", "linux-x86_64", "1.0"));

assertDependencies(bom, ArtifactCoords.jar("io.netty", "netty-tcnative-boringssl-static", "linux-x86_64", "1.0"),
ArtifactCoords.jar("io.netty", "netty-tcnative-boringssl-static", "linux-aarch_64", "1.0"),
ArtifactCoords.jar("io.netty", "netty-tcnative-boringssl-static", "osx-aarch_64", "1.0"),
ArtifactCoords.jar("io.netty", "netty-tcnative-boringssl-static", "osx-x86_64", "1.0"),
ArtifactCoords.jar("io.netty", "netty-tcnative-boringssl-static", "windows-x86_64", "1.0"));

assertDependencies(bom, ArtifactCoords.jar("io.netty", "netty-tcnative-boringssl-static", "linux-aarch_64", "1.0"));
assertDependencies(bom, ArtifactCoords.jar("io.netty", "netty-tcnative-boringssl-static", "osx-aarch_64", "1.0"));
assertDependencies(bom, ArtifactCoords.jar("io.netty", "netty-tcnative-boringssl-static", "osx-x86_64", "1.0"));
assertDependencies(bom, ArtifactCoords.jar("io.netty", "netty-tcnative-boringssl-static", "windows-x86_64", "1.0"));
}

private static void assertDependencies(Bom bom, ArtifactCoords compCoords, ArtifactCoords... expectedDeps) {
var purl = PurgingDependencyTreeVisitor.getPurl(compCoords).toString();
Component comp = null;
for (var c : bom.getComponents()) {
if (c.getPurl().equals(purl)) {
comp = c;
break;
}
}
if (comp == null) {
fail("Failed to locate " + purl);
return;
}
Dependency dep = null;
for (var d : bom.getDependencies()) {
if (d.getRef().equals(comp.getBomRef())) {
dep = d;
break;
}
}
if (dep == null && expectedDeps.length > 0) {
fail(comp.getBomRef() + " has no dependencies manifested while expected " + expectedDeps.length);
return;
}
final List<Dependency> recordedDeps = dep == null ? List.of() : dep.getDependencies();
var recordedDepPurls = new HashSet<String>(recordedDeps.size());
for (var d : recordedDeps) {
recordedDepPurls.add(d.getRef());
}
var expectedDepPurls = new HashSet<String>(expectedDeps.length);
for (var c : expectedDeps) {
expectedDepPurls.add(PurgingDependencyTreeVisitor.getPurl(c).toString());
}
assertThat(recordedDepPurls).isEqualTo(expectedDepPurls);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,18 +58,29 @@ private void install(TestModule module) {
} catch (IOException e) {
throw new RuntimeException(e);
}
try {
ModelUtils.persistModel(
artifactDir.resolve(module.getArtifactId() + "-" + module.getVersion() + "." + ArtifactCoords.TYPE_POM),
module.getModel());
} catch (IOException e) {
throw new RuntimeException(e);
}
if (ArtifactCoords.TYPE_JAR.equals(module.getPackaging())) {
try (var fs = ZipUtils.newZip(
artifactDir.resolve(module.getArtifactId() + "-" + module.getVersion() + "." + ArtifactCoords.TYPE_JAR))) {
} catch (IOException e) {
throw new RuntimeException(e);
for (var a : module.getPublishedArtifacts()) {
if (ArtifactCoords.TYPE_JAR.equals(a.getType())) {
var name = new StringBuilder();
name.append(module.getArtifactId()).append("-").append(module.getVersion());
if (!a.getClassifier().isEmpty()) {
name.append("-").append(a.getClassifier());
}
name.append(".").append(ArtifactCoords.TYPE_JAR);
try (var fs = ZipUtils.newZip(artifactDir.resolve(name.toString()))) {
} catch (IOException e) {
throw new RuntimeException(e);
}
} else if (ArtifactCoords.TYPE_POM.equals(a.getType())) {
try {
ModelUtils.persistModel(
artifactDir.resolve(
module.getArtifactId() + "-" + module.getVersion() + "." + ArtifactCoords.TYPE_POM),
module.getModel());
} catch (IOException e) {
throw new RuntimeException(e);
}
} else {
throw new IllegalStateException("Unsupported artifact type " + a);
}
}
for (TestModule m : module.getModules()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
import io.quarkus.maven.dependency.ArtifactCoords;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.DependencyManagement;
import org.apache.maven.model.Model;
Expand All @@ -26,6 +28,7 @@ public class TestModule {
private List<TestModule> modules = List.of();
private Map<String, List<ArtifactCoords>> dependenciesByScope = Map.of();
private Map<String, List<ArtifactCoords>> constraintsByScope = Map.of();
private Set<String> publishedClassifiers = Set.of();

TestModule(TestProject project, String artifactId) {
this.project = Objects.requireNonNull(project);
Expand Down Expand Up @@ -134,6 +137,10 @@ public TestModule addDependency(String groupId, String artifactId, String versio
return addDependency(ArtifactCoords.jar(groupId, artifactId, version), JavaScopes.COMPILE);
}

public TestModule addDependency(ArtifactCoords coords) {
return addDependency(coords, JavaScopes.COMPILE);
}

public TestModule addDependency(ArtifactCoords coords, String scope) {
if (dependenciesByScope.isEmpty()) {
dependenciesByScope = new LinkedHashMap<>();
Expand Down Expand Up @@ -183,6 +190,14 @@ public TestModule importBom(String groupId, String artifactId, String version) {
return addVersionConstraint(ArtifactCoords.pom(groupId, artifactId, version), "import");
}

public TestModule addClassifier(String classifier) {
if (publishedClassifiers.isEmpty()) {
publishedClassifiers = new HashSet<>();
}
publishedClassifiers.add(classifier);
return this;
}

public Model getModel() {
var model = new Model();
model.setModelVersion("4.0.0");
Expand Down Expand Up @@ -224,6 +239,23 @@ public Model getModel() {
return model;
}

public Collection<ArtifactCoords> getPublishedArtifacts() {
var artifacts = new ArrayList<ArtifactCoords>(1
+ (ArtifactCoords.TYPE_JAR.equals(packaging) ? 1 : 0)
+ publishedClassifiers.size());
var groupId = getGroupId();
var artifactId = getArtifactId();
var version = getVersion();
artifacts.add(ArtifactCoords.pom(groupId, artifactId, version));
if (ArtifactCoords.TYPE_JAR.equals(packaging)) {
artifacts.add(ArtifactCoords.jar(groupId, artifactId, version));
}
for (var classifier : publishedClassifiers) {
artifacts.add(ArtifactCoords.jar(groupId, artifactId, classifier, version));
}
return artifacts;
}

private static List<Dependency> toModelDeps(Map<String, List<ArtifactCoords>> deps) {
var result = new ArrayList<Dependency>();
for (Map.Entry<String, List<ArtifactCoords>> scopeDeps : deps.entrySet()) {
Expand Down

0 comments on commit 5444a86

Please sign in to comment.