Skip to content

Commit 69bf9e0

Browse files
author
Markus Paulsen
committed
Optimise ArchUnit transitive analysis
1 parent 640a976 commit 69bf9e0

4 files changed

Lines changed: 290 additions & 111 deletions

File tree

.github/workflows/maven.yml

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -140,22 +140,18 @@ jobs:
140140
method-suffixes: '*MavenArchunitAspectJ+*_archunit_aspectj'
141141
architecture-method-suffixes: '*MavenArchunitAspectJ_test'
142142
reuse-forks: 'true'
143-
run-core-tests: true
144143
- name: ArchUnit + instrumentation
145144
method-suffixes: '*MavenArchunitInstrumentation+*_archunit_instrumentation'
146145
architecture-method-suffixes: '*MavenArchunitInstrumentation_test'
147146
reuse-forks: 'true'
148-
run-core-tests: false
149147
- name: WALA + AspectJ
150148
method-suffixes: '*MavenWalaAspectJ+*_wala_aspectj'
151149
architecture-method-suffixes: '*MavenWalaAspectJ_test'
152150
reuse-forks: 'true'
153-
run-core-tests: false
154151
- name: WALA + instrumentation
155152
method-suffixes: '*MavenWalaInstrumentation+*_wala_instrumentation'
156153
architecture-method-suffixes: '*MavenWalaInstrumentation_test'
157154
reuse-forks: 'true'
158-
run-core-tests: false
159155
permissions:
160156
contents: read
161157
steps:
@@ -171,19 +167,36 @@ jobs:
171167
distribution: 'temurin'
172168
cache: 'maven'
173169

174-
# Non-matrix integration tests run once and remain separate from unit tests.
175-
- name: Core integration tests
176-
if: ${{ matrix.run-core-tests }}
177-
run: mvn test -Pintegration-core-tests -f pom.xml
178-
179-
- name: jqwik integration tests
180-
if: ${{ matrix.run-core-tests }}
181-
run: mvn test -f pom.xml -Dtest=de.tum.cit.ase.ares.integration.JqwickTest
182-
183170
# Method-level selection starts only the classes belonging to this mode. All
184171
# mode combinations reuse one fork; WALA state remains policy-scoped.
185172
- name: Test mode combination
186173
run: >-
187174
mvn test -f pom.xml
188175
-Dtest='de.tum.cit.ase.ares.integration.aop.allowed.*Test#${{ matrix.method-suffixes }},de.tum.cit.ase.ares.integration.aop.allowed.FileSystemAccessTest$*#${{ matrix.method-suffixes }},de.tum.cit.ase.ares.integration.aop.forbidden.*Test#${{ matrix.method-suffixes }},de.tum.cit.ase.ares.integration.architecture.forbidden.*Test#${{ matrix.architecture-method-suffixes }}'
189176
-Dsurefire-reuse-forks=${{ matrix.reuse-forks }}
177+
178+
core-integration-tests:
179+
name: Core Integration Tests
180+
needs: build
181+
runs-on: ubuntu-24.04
182+
timeout-minutes: 45
183+
permissions:
184+
contents: read
185+
steps:
186+
- name: Checkout Repository
187+
uses: actions/checkout@v7
188+
with:
189+
persist-credentials: false
190+
191+
- name: Set up JDK 21
192+
uses: actions/setup-java@v5
193+
with:
194+
java-version: '21'
195+
distribution: 'temurin'
196+
cache: 'maven'
197+
198+
- name: Core integration tests
199+
run: mvn test -Pintegration-core-tests -f pom.xml
200+
201+
- name: jqwik integration tests
202+
run: mvn test -f pom.xml -Dtest=de.tum.cit.ase.ares.integration.JqwickTest

src/main/java/de/tum/cit/ase/ares/api/architecture/java/archunit/JavaArchunitTestCaseCollection.java

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
//<editor-fold desc="Imports">
44
import java.nio.file.Path;
55
import java.util.Set;
6+
import java.util.stream.Collectors;
67

78
import com.tngtech.archunit.base.DescribedPredicate;
89
import com.tngtech.archunit.core.domain.JavaAccess;
@@ -76,16 +77,23 @@ private static ArchRule createNoClassShouldHaveMethodRule(String ruleName, Path
7677
Set<ClassPermission> allowedClasses) {
7778
return ArchRuleDefinition.noClasses().that(isNotAllowedClass(allowedClasses))
7879
.should(new TransitivelyAccessesMethodsCondition(new DescribedPredicate<>(ruleName) {
79-
private Set<String> forbiddenMethods;
80+
private Set<String> exactForbiddenMethods;
81+
82+
private Set<String> forbiddenMethodPrefixes;
8083

8184
@Override
8285
public boolean test(JavaAccess<?> javaAccess) {
83-
if (forbiddenMethods == null) {
84-
forbiddenMethods = ForbiddenMethodMatcher.effectiveMethods(methodsFilePath);
86+
if (exactForbiddenMethods == null) {
87+
Set<String> forbiddenMethods = ForbiddenMethodMatcher.effectiveMethods(methodsFilePath);
88+
exactForbiddenMethods = forbiddenMethods.stream().filter(method -> method.indexOf('(') >= 0)
89+
.collect(Collectors.toUnmodifiableSet());
90+
forbiddenMethodPrefixes = forbiddenMethods.stream()
91+
.filter(method -> method.indexOf('(') < 0).collect(Collectors.toUnmodifiableSet());
8592
}
86-
return forbiddenMethods.stream().filter(method -> !method.isEmpty())
87-
.anyMatch(method -> ForbiddenMethodMatcher
88-
.matches(convertArrayNotation(javaAccess.getTarget().getFullName()), method));
93+
String accessedMethod = ForbiddenMethodMatcher
94+
.canonicalise(convertArrayNotation(javaAccess.getTarget().getFullName()));
95+
return exactForbiddenMethods.contains(accessedMethod) || forbiddenMethodPrefixes.stream()
96+
.anyMatch(method -> ForbiddenMethodMatcher.matches(accessedMethod, method));
8997
}
9098
})).as(ruleName);
9199
}

0 commit comments

Comments
 (0)