Skip to content

Commit e3c73ec

Browse files
committed
Add new ComputeTypeUsageTest.
1 parent 14e50e8 commit e3c73ec

13 files changed

Lines changed: 981 additions & 0 deletions

File tree

api/api.txt

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ package com.autonomousapps {
44
public abstract class AbstractExtension {
55
ctor @javax.inject.Inject public AbstractExtension(org.gradle.api.model.ObjectFactory objects, org.gradle.api.invocation.Gradle gradle);
66
method public final org.gradle.api.file.RegularFileProperty adviceOutput();
7+
method public final org.gradle.api.file.RegularFileProperty typeUsageOutput();
78
method public final void app();
89
method public final void registerPostProcessingTask(org.gradle.api.tasks.TaskProvider<? extends com.autonomousapps.AbstractPostProcessingTask> task);
910
field public static final String NAME = "dependencyAnalysis";
@@ -28,6 +29,7 @@ package com.autonomousapps {
2829
method public final void issues(org.gradle.api.Action<com.autonomousapps.extension.IssueHandler> action);
2930
method public final void reporting(org.gradle.api.Action<com.autonomousapps.extension.ReportingHandler> action);
3031
method public final void structure(org.gradle.api.Action<com.autonomousapps.extension.DependenciesHandler> action);
32+
method public final void typeUsage(org.gradle.api.Action<com.autonomousapps.extension.TypeUsageHandler> action);
3133
method public final void usage(org.gradle.api.Action<com.autonomousapps.extension.UsageHandler> action);
3234
method @Deprecated public final void usages(org.gradle.api.Action<com.autonomousapps.extension.UsageHandler> action);
3335
method public final void useTypesafeProjectAccessors(boolean enable);
@@ -44,6 +46,7 @@ package com.autonomousapps {
4446
method public final void abi(org.gradle.api.Action<com.autonomousapps.extension.AbiHandler> action);
4547
method public final void issues(org.gradle.api.Action<com.autonomousapps.extension.ProjectIssueHandler> action);
4648
method public final void structure(org.gradle.api.Action<com.autonomousapps.extension.DependenciesHandler> action);
49+
method public final void typeUsage(org.gradle.api.Action<com.autonomousapps.extension.TypeUsageHandler> action);
4750
}
4851

4952
public final class Flags {
@@ -209,6 +212,13 @@ package com.autonomousapps.extension {
209212
property @org.gradle.api.tasks.Input public abstract org.gradle.api.provider.Property<java.lang.String> postscript;
210213
}
211214

215+
public abstract class TypeUsageHandler {
216+
ctor @javax.inject.Inject public TypeUsageHandler(org.gradle.api.model.ObjectFactory objects);
217+
method public final void excludePackages(java.lang.String... packages);
218+
method public final void excludeRegex(@org.intellij.lang.annotations.Language("RegExp") java.lang.String... patterns);
219+
method public final void excludeTypes(java.lang.String... types);
220+
}
221+
212222
public abstract class SourceSetsHandler implements org.gradle.api.Named {
213223
ctor @javax.inject.Inject public SourceSetsHandler(String sourceSetName, String projectPath, org.gradle.api.model.ObjectFactory objects);
214224
method public String getName();
@@ -509,6 +519,48 @@ package com.autonomousapps.model {
509519
property public String identifier;
510520
}
511521

522+
@com.squareup.moshi.JsonClass(generateAdapter=false) public final class ProjectTypeUsage implements java.lang.Comparable<com.autonomousapps.model.ProjectTypeUsage> {
523+
ctor public ProjectTypeUsage(String projectPath, com.autonomousapps.model.TypeUsageSummary summary, java.util.Map<java.lang.String,java.lang.Integer> internal, java.util.Map<java.lang.String,? extends java.util.Map<java.lang.String,java.lang.Integer>> projectDependencies, java.util.Map<java.lang.String,? extends java.util.Map<java.lang.String,java.lang.Integer>> libraryDependencies);
524+
method public int compareTo(com.autonomousapps.model.ProjectTypeUsage other);
525+
method public String component1();
526+
method public com.autonomousapps.model.TypeUsageSummary component2();
527+
method public java.util.Map<java.lang.String,java.lang.Integer> component3();
528+
method public java.util.Map<java.lang.String,java.util.Map<java.lang.String,java.lang.Integer>> component4();
529+
method public java.util.Map<java.lang.String,java.util.Map<java.lang.String,java.lang.Integer>> component5();
530+
method public com.autonomousapps.model.ProjectTypeUsage copy(String projectPath, com.autonomousapps.model.TypeUsageSummary summary, java.util.Map<java.lang.String,java.lang.Integer> internal, java.util.Map<java.lang.String,? extends java.util.Map<java.lang.String,java.lang.Integer>> projectDependencies, java.util.Map<java.lang.String,? extends java.util.Map<java.lang.String,java.lang.Integer>> libraryDependencies);
531+
method public java.util.Map<java.lang.String,java.lang.Integer> getInternal();
532+
method public java.util.Map<java.lang.String,java.util.Map<java.lang.String,java.lang.Integer>> getLibraryDependencies();
533+
method public java.util.Map<java.lang.String,java.util.Map<java.lang.String,java.lang.Integer>> getProjectDependencies();
534+
method public String getProjectPath();
535+
method public com.autonomousapps.model.TypeUsageSummary getSummary();
536+
method public boolean isEmpty();
537+
property public final java.util.Map<java.lang.String,java.lang.Integer> internal;
538+
property public final java.util.Map<java.lang.String,java.util.Map<java.lang.String,java.lang.Integer>> libraryDependencies;
539+
property public final java.util.Map<java.lang.String,java.util.Map<java.lang.String,java.lang.Integer>> projectDependencies;
540+
property public final String projectPath;
541+
property public final com.autonomousapps.model.TypeUsageSummary summary;
542+
}
543+
544+
@com.squareup.moshi.JsonClass(generateAdapter=false) public final class TypeUsageSummary {
545+
ctor public TypeUsageSummary(int totalTypes, int totalFiles, int internalTypes, int projectDependencies, int libraryDependencies);
546+
method public int component1();
547+
method public int component2();
548+
method public int component3();
549+
method public int component4();
550+
method public int component5();
551+
method public com.autonomousapps.model.TypeUsageSummary copy(int totalTypes, int totalFiles, int internalTypes, int projectDependencies, int libraryDependencies);
552+
method public int getInternalTypes();
553+
method public int getLibraryDependencies();
554+
method public int getProjectDependencies();
555+
method public int getTotalFiles();
556+
method public int getTotalTypes();
557+
property public final int internalTypes;
558+
property public final int libraryDependencies;
559+
property public final int projectDependencies;
560+
property public final int totalFiles;
561+
property public final int totalTypes;
562+
}
563+
512564
@com.squareup.moshi.JsonClass(generateAdapter=false) public final class Warning implements java.lang.Comparable<com.autonomousapps.model.Warning> {
513565
ctor public Warning(java.util.Set<com.autonomousapps.model.DuplicateClass> duplicateClasses);
514566
method public int compareTo(com.autonomousapps.model.Warning other);
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
// Copyright (c) 2026. Tony Robalik.
2+
// SPDX-License-Identifier: Apache-2.0
3+
package com.autonomousapps.jvm
4+
5+
import com.autonomousapps.jvm.projects.TypeUsageProject
6+
import com.autonomousapps.jvm.projects.TypeUsageWithFiltersProject
7+
import com.autonomousapps.jvm.projects.TypeUsageMultiModuleProject
8+
9+
import static com.autonomousapps.utils.Runner.build
10+
import static com.google.common.truth.Truth.assertThat
11+
12+
final class TypeUsageSpec extends AbstractJvmSpec {
13+
14+
def "generates type usage report (#gradleVersion)"() {
15+
given:
16+
def project = new TypeUsageProject()
17+
gradleProject = project.gradleProject
18+
19+
when:
20+
build(gradleVersion, gradleProject.rootDir, 'computeTypeUsageMain')
21+
22+
then: 'has correct summary'
23+
def usage = project.actualTypeUsage()
24+
assertThat(usage.projectPath).isEqualTo(':proj')
25+
assertThat(usage.summary.totalTypes).isGreaterThan(0)
26+
assertThat(usage.summary.totalFiles).isEqualTo(2)
27+
assertThat(usage.summary.internalTypes).isEqualTo(1)
28+
29+
and: 'tracks internal usage'
30+
assertThat(usage.internal).containsKey('com.example.Example')
31+
// Note: Internal class is not tracked because it's defined but never used
32+
33+
and: 'tracks library dependencies'
34+
assertThat(usage.libraryDependencies).isNotEmpty()
35+
36+
and: 'tracks commons-collections usage'
37+
assertThat(usage.libraryDependencies)
38+
.containsKey('org.apache.commons:commons-collections4')
39+
def commonsUsage = usage.libraryDependencies.get('org.apache.commons:commons-collections4')
40+
assertThat(commonsUsage).containsKey('org.apache.commons.collections4.bag.HashBag')
41+
42+
and: 'tracks kotlin stdlib usage'
43+
assert usage.libraryDependencies.keySet().any { it.startsWith('org.jetbrains.kotlin:kotlin-stdlib') }
44+
45+
where:
46+
gradleVersion << gradleVersions()
47+
}
48+
49+
def "excludes filtered types (#gradleVersion)"() {
50+
given:
51+
def project = new TypeUsageWithFiltersProject()
52+
gradleProject = project.gradleProject
53+
54+
when:
55+
build(gradleVersion, gradleProject.rootDir, 'computeTypeUsageMain')
56+
57+
then: 'excluded packages are not present'
58+
def usage = project.actualTypeUsage()
59+
def allTypes = usage.libraryDependencies.values()
60+
.collectMany { it.keySet() }
61+
62+
assertThat(allTypes).doesNotContain('org.apache.commons.collections4.bag.HashBag')
63+
64+
and: 'excluded types are not present'
65+
assertThat(allTypes).doesNotContain('kotlin.Unit')
66+
67+
and: 'non-excluded types are still present'
68+
assertThat(usage.internal).containsKey('com.example.Example')
69+
70+
where:
71+
gradleVersion << gradleVersions()
72+
}
73+
74+
def "categorizes dependencies correctly (#gradleVersion)"() {
75+
given:
76+
def project = new TypeUsageProject()
77+
gradleProject = project.gradleProject
78+
79+
when:
80+
build(gradleVersion, gradleProject.rootDir, 'computeTypeUsageMain')
81+
82+
then: 'internal types are in internal map'
83+
def usage = project.actualTypeUsage()
84+
assertThat(usage.internal).isNotEmpty()
85+
assertThat(usage.internal).containsKey('com.example.Example')
86+
87+
and: 'library types are in libraryDependencies map'
88+
assertThat(usage.libraryDependencies).isNotEmpty()
89+
90+
and: 'no project dependencies (single-project)'
91+
assertThat(usage.projectDependencies).isEmpty()
92+
93+
and: 'summary counts match'
94+
assertThat(usage.summary.internalTypes).isEqualTo(usage.internal.size())
95+
assertThat(usage.summary.libraryDependencies).isEqualTo(usage.libraryDependencies.size())
96+
assertThat(usage.summary.projectDependencies).isEqualTo(0)
97+
98+
where:
99+
gradleVersion << gradleVersions()
100+
}
101+
102+
def "tracks type usage across multiple modules (#gradleVersion)"() {
103+
given:
104+
def project = new TypeUsageMultiModuleProject()
105+
gradleProject = project.gradleProject
106+
107+
when:
108+
build(gradleVersion, gradleProject.rootDir, 'computeTypeUsageMain')
109+
110+
then: 'app module tracks project dependencies'
111+
def appUsage = project.actualTypeUsageFor(':app')
112+
assertThat(appUsage.projectPath).isEqualTo(':app')
113+
assertThat(appUsage.projectDependencies).hasSize(2)
114+
assertThat(appUsage.projectDependencies).containsKey(':core')
115+
assertThat(appUsage.projectDependencies).containsKey(':utils')
116+
117+
and: 'app tracks types from core'
118+
def coreTypes = appUsage.projectDependencies[':core']
119+
assertThat(coreTypes).containsKey('com.example.core.UserRepository')
120+
assertThat(coreTypes).containsKey('com.example.core.User')
121+
122+
and: 'app tracks types from utils'
123+
def utilsTypes = appUsage.projectDependencies[':utils']
124+
assertThat(utilsTypes).containsKey('com.example.utils.Logger')
125+
126+
and: 'app tracks library dependencies'
127+
assertThat(appUsage.libraryDependencies).containsKey('org.apache.commons:commons-collections4')
128+
129+
and: 'app tracks internal types'
130+
assertThat(appUsage.internal).containsKey('com.example.app.MainActivity')
131+
132+
where:
133+
gradleVersion << gradleVersions()
134+
}
135+
136+
def "core module tracks its dependencies (#gradleVersion)"() {
137+
given:
138+
def project = new TypeUsageMultiModuleProject()
139+
gradleProject = project.gradleProject
140+
141+
when:
142+
build(gradleVersion, gradleProject.rootDir, 'computeTypeUsageMain')
143+
144+
then: 'core tracks utils as project dependency'
145+
def coreUsage = project.actualTypeUsageFor(':core')
146+
assertThat(coreUsage.projectPath).isEqualTo(':core')
147+
assertThat(coreUsage.projectDependencies).hasSize(1)
148+
assertThat(coreUsage.projectDependencies).containsKey(':utils')
149+
150+
and: 'core tracks Logger from utils'
151+
def utilsTypes = coreUsage.projectDependencies[':utils']
152+
assertThat(utilsTypes).containsKey('com.example.utils.Logger')
153+
154+
and: 'core tracks its own internal types'
155+
assertThat(coreUsage.internal).containsKey('com.example.core.User')
156+
157+
and: 'core has no library dependencies beyond kotlin stdlib'
158+
def nonKotlinLibs = coreUsage.libraryDependencies.keySet().findAll {
159+
!it.startsWith('org.jetbrains')
160+
}
161+
assertThat(nonKotlinLibs).isEmpty()
162+
163+
where:
164+
gradleVersion << gradleVersions()
165+
}
166+
167+
def "utils module has only library dependencies (#gradleVersion)"() {
168+
given:
169+
def project = new TypeUsageMultiModuleProject()
170+
gradleProject = project.gradleProject
171+
172+
when:
173+
build(gradleVersion, gradleProject.rootDir, 'computeTypeUsageMain')
174+
175+
then: 'utils has no project dependencies'
176+
def utilsUsage = project.actualTypeUsageFor(':utils')
177+
assertThat(utilsUsage.projectPath).isEqualTo(':utils')
178+
assertThat(utilsUsage.projectDependencies).isEmpty()
179+
180+
and: 'utils tracks commons-io'
181+
assertThat(utilsUsage.libraryDependencies).containsKey('commons-io:commons-io')
182+
def commonsIoTypes = utilsUsage.libraryDependencies['commons-io:commons-io']
183+
assertThat(commonsIoTypes).containsKey('org.apache.commons.io.FileUtils')
184+
185+
and: 'utils tracks internal Logger type'
186+
assertThat(utilsUsage.internal).containsKey('com.example.utils.Logger')
187+
188+
and: 'summary counts are correct'
189+
assertThat(utilsUsage.summary.projectDependencies).isEqualTo(0)
190+
assertThat(utilsUsage.summary.libraryDependencies).isGreaterThan(0)
191+
assertThat(utilsUsage.summary.internalTypes).isEqualTo(utilsUsage.internal.size())
192+
193+
where:
194+
gradleVersion << gradleVersions()
195+
}
196+
}

0 commit comments

Comments
 (0)