diff --git a/src/main/java/me/champeau/gradle/japicmp/JApiCmpWorkerAction.java b/src/main/java/me/champeau/gradle/japicmp/JApiCmpWorkerAction.java index 33b0ecf..9d700b5 100644 --- a/src/main/java/me/champeau/gradle/japicmp/JApiCmpWorkerAction.java +++ b/src/main/java/me/champeau/gradle/japicmp/JApiCmpWorkerAction.java @@ -74,6 +74,7 @@ import java.util.Comparator; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.stream.Collectors; public class JApiCmpWorkerAction extends JapiCmpWorkerConfiguration implements Runnable { @@ -410,5 +411,18 @@ public String toString() { public JApiCmpArchive toJapicmpArchive() { return new JApiCmpArchive(file, version); } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + return obj instanceof Archive && hashCode() == obj.hashCode(); + } + + @Override + public int hashCode() { + return Objects.hash(file.getName(), version); + } } } diff --git a/src/main/java/me/champeau/gradle/japicmp/JapicmpTask.java b/src/main/java/me/champeau/gradle/japicmp/JapicmpTask.java index dc7fe50..5810ea5 100644 --- a/src/main/java/me/champeau/gradle/japicmp/JapicmpTask.java +++ b/src/main/java/me/champeau/gradle/japicmp/JapicmpTask.java @@ -35,8 +35,10 @@ import java.security.CodeSource; import java.security.ProtectionDomain; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Set; @@ -225,15 +227,12 @@ private static List toArchives(FileCollection fc) { return archives; } - private void collectArchives(final List archives, ResolvedDependency resolvedDependency) { - String version = resolvedDependency.getModule().getId().getVersion(); + private void collectArchives(final Collection archives, ResolvedDependency resolvedDependency) { Set allModuleArtifacts = resolvedDependency.getAllModuleArtifacts(); for (ResolvedArtifact resolvedArtifact : allModuleArtifacts) { + String version = resolvedArtifact.getModuleVersion().getId().getVersion(); archives.add(new JApiCmpWorkerAction.Archive(resolvedArtifact.getFile(), version)); } - for (ResolvedDependency dependency : resolvedDependency.getChildren()) { - collectArchives(archives, dependency); - } } public void richReport(Action configureAction) { @@ -308,7 +307,7 @@ public void addExcludeFilter(Class excludeFilterClass) { public abstract ListProperty getOldArchiveList(); public void addOldArchives(Configuration config) { - List oldArchives = new ArrayList<>(); + Set oldArchives = new LinkedHashSet<>(); for (ResolvedDependency resolvedDependency : config.getResolvedConfiguration().getFirstLevelModuleDependencies()) { collectArchives(oldArchives, resolvedDependency); } @@ -320,7 +319,7 @@ public void addOldArchives(Configuration config) { public abstract ListProperty getNewArchiveList(); public void addNewArchives(Configuration config) { - List newArchives = new ArrayList<>(); + Set newArchives = new LinkedHashSet<>(); for (ResolvedDependency resolvedDependency : config.getResolvedConfiguration().getFirstLevelModuleDependencies()) { collectArchives(newArchives, resolvedDependency); } diff --git a/src/test/groovy/me/champeau/gradle/ReportsFunctionalTest.groovy b/src/test/groovy/me/champeau/gradle/ReportsFunctionalTest.groovy index 6984d57..4930893 100644 --- a/src/test/groovy/me/champeau/gradle/ReportsFunctionalTest.groovy +++ b/src/test/groovy/me/champeau/gradle/ReportsFunctionalTest.groovy @@ -81,6 +81,30 @@ class ReportsFunctionalTest extends BaseFunctionalTest { result.task(":japicmpXmlWithVersions").outcome == TaskOutcome.UP_TO_DATE } + def "can generate an XML report with no duplicate transitive versions"() { + when: + def result = run 'japicmpXmlWithTransitiveVersions' + + then: + result.task(":japicmpXmlWithTransitiveVersions").outcome == TaskOutcome.SUCCESS + hasXmlReport('oldJar="assertj-guava-3.25.3.jar;assertj-core-3.25.3.jar;byte-buddy-1.14.11.jar;assertj-joda-time-2.2.0.jar"') + hasXmlReport('newJar="assertj-guava-3.26.3.jar;assertj-core-3.26.3.jar;byte-buddy-1.14.18.jar;assertj-joda-time-2.1.0.jar"') + hasXmlReport('oldVersion="3.25.3;3.25.3;1.14.11;2.2.0"') + hasXmlReport('newVersion="3.26.3;3.26.3;1.14.18;2.1.0"') + + noTxtReport() + noMarkdownReport() + noSemverReport() + noHtmlReport() + noRichReport() + + when: + result = run 'japicmpXmlWithTransitiveVersions' + + then: + result.task(":japicmpXmlWithTransitiveVersions").outcome == TaskOutcome.UP_TO_DATE + } + def "can generate rich report"() { when: def result = run 'japicmpRich' diff --git a/src/test/test-projects/html-report/build.gradle b/src/test/test-projects/html-report/build.gradle index 889426e..6f2b92e 100644 --- a/src/test/test-projects/html-report/build.gradle +++ b/src/test/test-projects/html-report/build.gradle @@ -9,11 +9,18 @@ repositories { configurations { baseline current + baseline2 + current2 } dependencies { baseline 'org.apache.commons:commons-lang3:3.5' current 'org.apache.commons:commons-lang3:3.6' + + baseline2 'org.assertj:assertj-guava:3.25.3' + baseline2 'org.assertj:assertj-joda-time:2.2.0' + current2 'org.assertj:assertj-guava:3.26.3' + current2 'org.assertj:assertj-joda-time:2.1.0' } task japicmp(type: me.champeau.gradle.japicmp.JapicmpTask) { @@ -61,3 +68,10 @@ task japicmpXmlWithVersions(type: me.champeau.gradle.japicmp.JapicmpTask) { addNewArchives(configurations.current) xmlOutputFile = layout.buildDirectory.file('reports/japi.xml').get().asFile } + +task japicmpXmlWithTransitiveVersions(type: me.champeau.gradle.japicmp.JapicmpTask) { + addOldArchives(configurations.baseline2) + addNewArchives(configurations.current2) + ignoreMissingClasses = true + xmlOutputFile = layout.buildDirectory.file('reports/japi.xml').get().asFile +}