From 3242bf6790708d832feb5a338a99ba94a01d975b Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Sat, 18 Sep 2021 16:47:01 +0200 Subject: [PATCH] Make ReflectionSupport.findNestedClasses thread-safe ReflectionSupport.findNestedClasses(..)` is now thread-safe with regard to cycle detection. Closes #2715 --- .../release-notes/release-notes-5.8.1.adoc | 3 ++- .../platform/commons/util/ReflectionUtils.java | 4 ++-- .../commons/util/ReflectionUtilsTests.java | 14 +++++++++----- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-5.8.1.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-5.8.1.adoc index aa84ee50e9da..f0d11ec07de0 100644 --- a/documentation/src/docs/asciidoc/release-notes/release-notes-5.8.1.adoc +++ b/documentation/src/docs/asciidoc/release-notes/release-notes-5.8.1.adoc @@ -23,7 +23,8 @@ GitHub. ==== New Features and Improvements -* ❓ +* `ReflectionSupport.findNestedClasses(..)` is now thread-safe with regard to cycle + detection. [[release-notes-5.8.1-junit-jupiter]] diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ReflectionUtils.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ReflectionUtils.java index e359120257b6..449d48a13744 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ReflectionUtils.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ReflectionUtils.java @@ -41,13 +41,13 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.IdentityHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.Predicate; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -130,7 +130,7 @@ public enum HierarchyTraversalMode { * @since 1.6 * @see #detectInnerClassCycle(Class) */ - private static final Set noCyclesDetectedCache = new HashSet<>(); + private static final Set noCyclesDetectedCache = ConcurrentHashMap.newKeySet(); /** * Internal cache of common class names mapped to their types. diff --git a/platform-tests/src/test/java/org/junit/platform/commons/util/ReflectionUtilsTests.java b/platform-tests/src/test/java/org/junit/platform/commons/util/ReflectionUtilsTests.java index 08fc346820db..0d2e2e60fb43 100644 --- a/platform-tests/src/test/java/org/junit/platform/commons/util/ReflectionUtilsTests.java +++ b/platform-tests/src/test/java/org/junit/platform/commons/util/ReflectionUtilsTests.java @@ -45,6 +45,7 @@ import java.util.function.Predicate; import java.util.logging.Level; import java.util.logging.LogRecord; +import java.util.stream.Stream; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -787,11 +788,14 @@ void findNestedClassesWithSeeminglyRecursiveHierarchies() { */ @Test void findNestedClassesWithRecursiveHierarchies() { - assertNestedCycle(OuterClass.class, InnerClass.class, OuterClass.class); - assertNestedCycle(StaticNestedClass.class, InnerClass.class, OuterClass.class); - assertNestedCycle(RecursiveInnerClass.class, OuterClass.class); - assertNestedCycle(RecursiveInnerInnerClass.class, OuterClass.class); - assertNestedCycle(InnerClass.class, RecursiveInnerInnerClass.class, OuterClass.class); + Runnable runnable1 = () -> assertNestedCycle(OuterClass.class, InnerClass.class, OuterClass.class); + Runnable runnable2 = () -> assertNestedCycle(StaticNestedClass.class, InnerClass.class, OuterClass.class); + Runnable runnable3 = () -> assertNestedCycle(RecursiveInnerClass.class, OuterClass.class); + Runnable runnable4 = () -> assertNestedCycle(RecursiveInnerInnerClass.class, OuterClass.class); + Runnable runnable5 = () -> assertNestedCycle(InnerClass.class, RecursiveInnerInnerClass.class, + OuterClass.class); + Stream.of(runnable1, runnable1, runnable1, runnable2, runnable2, runnable2, runnable3, runnable3, runnable3, + runnable4, runnable4, runnable4, runnable5, runnable5, runnable5).parallel().forEach(Runnable::run); } private static List> findNestedClasses(Class clazz) {