From 982a5b469f5dad34fdffac1bfb5ff3973ba4ec98 Mon Sep 17 00:00:00 2001 From: Stefan Oehme Date: Fri, 4 Sep 2026 11:56:04 +0200 Subject: [PATCH] Make ArchUnit compatible with Gradle test retry plugin Gradle always uses individual ClassSelectors for each test class in the test task. The test retry plugin then layers filters on top to include only the tests it wants to rerun. This change ensures these filters are actually honored. Without this, all ArchUnit tests are rerun, regardless if they failed. Fixes #1711 Signed-off-by: Stefan Oehme --- .../archunit/junit/internal/ArchUnitTestEngine.java | 7 ++++--- .../junit/internal/ArchUnitTestEngineTest.java | 13 +++++++++++++ .../archunit/junit/internal/ArchUnitTestEngine.java | 7 ++++--- .../junit/internal/ArchUnitTestEngineTest.java | 13 +++++++++++++ 4 files changed, 34 insertions(+), 6 deletions(-) diff --git a/archunit-junit/junit5/engine/src/main/java/com/tngtech/archunit/junit/internal/ArchUnitTestEngine.java b/archunit-junit/junit5/engine/src/main/java/com/tngtech/archunit/junit/internal/ArchUnitTestEngine.java index c0fcd0e8f..c528d8827 100644 --- a/archunit-junit/junit5/engine/src/main/java/com/tngtech/archunit/junit/internal/ArchUnitTestEngine.java +++ b/archunit-junit/junit5/engine/src/main/java/com/tngtech/archunit/junit/internal/ArchUnitTestEngine.java @@ -118,7 +118,7 @@ private void resolveRequestedPackages(EngineDiscoveryRequest discoveryRequest, U private Stream> filterCandidatesAndLoadClasses(Stream classes, EngineDiscoveryRequest discoveryRequest) { return classes - .filter(isAllowedBy(discoveryRequest)) + .filter(isAllowedBy(discoveryRequest, JavaClass::getName)) .filter(this::isArchUnitTestCandidate) .flatMap(this::safelyReflect); } @@ -126,6 +126,7 @@ private Stream> filterCandidatesAndLoadClasses(Stream classe private void resolveRequestedClasses(EngineDiscoveryRequest discoveryRequest, UniqueId uniqueId, ArchUnitEngineDescriptor result) { discoveryRequest.getSelectorsByType(ClassSelector.class).stream() .map(ClassSelector::getJavaClass) + .filter(isAllowedBy(discoveryRequest, Class::getName)) .filter(this::isArchUnitTestCandidate) .forEach(clazz -> ArchUnitTestDescriptor.resolve( result, ElementResolver.create(result, uniqueId, clazz), cache.get())); @@ -170,14 +171,14 @@ private Stream discoverClasses(Function isAllowedBy(EngineDiscoveryRequest discoveryRequest) { + private Predicate isAllowedBy(EngineDiscoveryRequest discoveryRequest, Function nameOf) { List> filters = Stream .concat(discoveryRequest.getFiltersByType(ClassNameFilter.class).stream(), discoveryRequest.getFiltersByType(PackageNameFilter.class).stream()) .map(Filter::toPredicate) .collect(toList()); - return javaClass -> filters.stream().allMatch(p -> p.test(javaClass.getName())); + return candidate -> filters.stream().allMatch(p -> p.test(nameOf.apply(candidate))); } private boolean isArchUnitTestCandidate(JavaClass javaClass) { diff --git a/archunit-junit/junit5/engine/src/test/java/com/tngtech/archunit/junit/internal/ArchUnitTestEngineTest.java b/archunit-junit/junit5/engine/src/test/java/com/tngtech/archunit/junit/internal/ArchUnitTestEngineTest.java index b31b07aad..e9ad41401 100644 --- a/archunit-junit/junit5/engine/src/test/java/com/tngtech/archunit/junit/internal/ArchUnitTestEngineTest.java +++ b/archunit-junit/junit5/engine/src/test/java/com/tngtech/archunit/junit/internal/ArchUnitTestEngineTest.java @@ -742,6 +742,19 @@ void filtering_included_class_names() { simpleRulesId(engineId)); } + @Test + void filtering_class_selectors_by_class_name() { + EngineDiscoveryTestRequest discoveryRequest = new EngineDiscoveryTestRequest() + .withClass(SimpleRuleField.class) + .withClass(SimpleRuleMethod.class) + .withClassNameFilter(excludeClassNamePatterns(".*Field.*")); + + TestDescriptor rootDescriptor = testEngine.discover(discoveryRequest, engineId); + + assertThat(toUniqueIds(rootDescriptor)).containsOnly( + engineId.append(CLASS_SEGMENT_TYPE, SimpleRuleMethod.class.getName())); + } + @Test void filtering_excluded_packages() { EngineDiscoveryTestRequest discoveryRequest = new EngineDiscoveryTestRequest() diff --git a/archunit-junit/junit6/engine/src/main/java/com/tngtech/archunit/junit/internal/ArchUnitTestEngine.java b/archunit-junit/junit6/engine/src/main/java/com/tngtech/archunit/junit/internal/ArchUnitTestEngine.java index b48965167..1273f739c 100644 --- a/archunit-junit/junit6/engine/src/main/java/com/tngtech/archunit/junit/internal/ArchUnitTestEngine.java +++ b/archunit-junit/junit6/engine/src/main/java/com/tngtech/archunit/junit/internal/ArchUnitTestEngine.java @@ -118,7 +118,7 @@ private void resolveRequestedPackages(EngineDiscoveryRequest discoveryRequest, U private Stream> filterCandidatesAndLoadClasses(Stream classes, EngineDiscoveryRequest discoveryRequest) { return classes - .filter(isAllowedBy(discoveryRequest)) + .filter(isAllowedBy(discoveryRequest, JavaClass::getName)) .filter(this::isArchUnitTestCandidate) .flatMap(this::safelyReflect); } @@ -126,6 +126,7 @@ private Stream> filterCandidatesAndLoadClasses(Stream classe private void resolveRequestedClasses(EngineDiscoveryRequest discoveryRequest, UniqueId uniqueId, ArchUnitEngineDescriptor result) { discoveryRequest.getSelectorsByType(ClassSelector.class).stream() .map(ClassSelector::getJavaClass) + .filter(isAllowedBy(discoveryRequest, Class::getName)) .filter(this::isArchUnitTestCandidate) .forEach(clazz -> ArchUnitTestDescriptor.resolve( result, ElementResolver.create(result, uniqueId, clazz), cache.get())); @@ -170,14 +171,14 @@ private Stream discoverClasses(Function isAllowedBy(EngineDiscoveryRequest discoveryRequest) { + private Predicate isAllowedBy(EngineDiscoveryRequest discoveryRequest, Function nameOf) { List> filters = Stream .concat(discoveryRequest.getFiltersByType(ClassNameFilter.class).stream(), discoveryRequest.getFiltersByType(PackageNameFilter.class).stream()) .map(Filter::toPredicate) .collect(toList()); - return javaClass -> filters.stream().allMatch(p -> p.test(javaClass.getName())); + return candidate -> filters.stream().allMatch(p -> p.test(nameOf.apply(candidate))); } private boolean isArchUnitTestCandidate(JavaClass javaClass) { diff --git a/archunit-junit/junit6/engine/src/test/java/com/tngtech/archunit/junit/internal/ArchUnitTestEngineTest.java b/archunit-junit/junit6/engine/src/test/java/com/tngtech/archunit/junit/internal/ArchUnitTestEngineTest.java index b31b07aad..e9ad41401 100644 --- a/archunit-junit/junit6/engine/src/test/java/com/tngtech/archunit/junit/internal/ArchUnitTestEngineTest.java +++ b/archunit-junit/junit6/engine/src/test/java/com/tngtech/archunit/junit/internal/ArchUnitTestEngineTest.java @@ -742,6 +742,19 @@ void filtering_included_class_names() { simpleRulesId(engineId)); } + @Test + void filtering_class_selectors_by_class_name() { + EngineDiscoveryTestRequest discoveryRequest = new EngineDiscoveryTestRequest() + .withClass(SimpleRuleField.class) + .withClass(SimpleRuleMethod.class) + .withClassNameFilter(excludeClassNamePatterns(".*Field.*")); + + TestDescriptor rootDescriptor = testEngine.discover(discoveryRequest, engineId); + + assertThat(toUniqueIds(rootDescriptor)).containsOnly( + engineId.append(CLASS_SEGMENT_TYPE, SimpleRuleMethod.class.getName())); + } + @Test void filtering_excluded_packages() { EngineDiscoveryTestRequest discoveryRequest = new EngineDiscoveryTestRequest()