Skip to content

Commit 5b8d5fe

Browse files
committed
Avoid infinite loops when registering test classes for reflection
1 parent 2c2ec24 commit 5b8d5fe

File tree

2 files changed

+50
-0
lines changed

2 files changed

+50
-0
lines changed

common/junit-platform-native/src/main/java/org/graalvm/junit/platform/JUnitPlatformFeature.java

+23
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,11 @@
6666
import java.io.UncheckedIOException;
6767
import java.nio.file.Files;
6868
import java.nio.file.Path;
69+
import java.util.HashSet;
6970
import java.util.List;
7071
import java.util.Optional;
7172
import java.util.ServiceLoader;
73+
import java.util.Set;
7274
import java.util.function.BiConsumer;
7375
import java.util.function.Consumer;
7476
import java.util.stream.Collectors;
@@ -168,7 +170,28 @@ private void registerTestClassesForReflection(List<? extends DiscoverySelector>
168170
.forEach(this::registerTestClassForReflection);
169171
}
170172

173+
private final Set<Class<?>> registeredClasses = new HashSet<>();
174+
175+
private boolean shouldRegisterClass(Class<?> clazz) {
176+
/* avoid registering java internal classes */
177+
if (ModuleLayer.boot().modules().contains(clazz.getModule())) {
178+
return false;
179+
}
180+
181+
/* avoid loops (possible case: class B is inner class of A, and B extends A) */
182+
if (registeredClasses.contains(clazz)) {
183+
return false;
184+
}
185+
registeredClasses.add(clazz);
186+
187+
return true;
188+
}
189+
171190
private void registerTestClassForReflection(Class<?> clazz) {
191+
if (!shouldRegisterClass(clazz)) {
192+
return;
193+
}
194+
172195
debug("Registering test class for reflection: %s", clazz.getName());
173196
nativeImageConfigImpl.registerAllClassMembersForReflection(clazz);
174197
forEachProvider(p -> p.onTestClassRegistered(clazz, nativeImageConfigImpl));

common/junit-platform-native/src/test/java/org/graalvm/junit/jupiter/AbstractParentClassTests.java

+27
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343

4444
import org.junit.jupiter.api.Assertions;
4545
import org.junit.jupiter.api.BeforeAll;
46+
import org.junit.jupiter.api.Test;
4647
import org.junit.jupiter.params.ParameterizedTest;
4748
import org.junit.jupiter.params.provider.Arguments;
4849
import org.junit.jupiter.params.provider.MethodSource;
@@ -52,6 +53,32 @@
5253

5354
public class AbstractParentClassTests {
5455

56+
public static class OuterClass {
57+
@Test
58+
protected void test() {
59+
Assertions.assertTrue(true, "Just a dummy test that should be executed in outer class");
60+
}
61+
62+
/* Since at org.graalvm.junit.platform.JUnitPlatformFeature#registerTestClassForReflection we register all
63+
* declared classes and superclass of the test class, and we do so recursively, we want to avoid infinite loop.
64+
* This inheritance shows that we won't call registration of these classes indefinitely (call registration of
65+
* all declared classes of AbstractParentClassTests, then recursively call superclass of InfiniteLoopTest and
66+
* repeat the process indefinitely) */
67+
private class InfiniteLoopTest extends OuterClass {
68+
@Test
69+
protected void test() {
70+
Assertions.assertTrue(true, "Just a dummy test that should be executed in inner class");
71+
}
72+
}
73+
74+
/* Since enum here is declared class of AbstractParentClassTests, we want to avoid registrations of
75+
* enum's internal superclasses and sub-classes at org.graalvm.junit.platform.JUnitPlatformFeature#registerTestClassForReflection */
76+
private enum EnumTest {
77+
SOME_VALUE,
78+
OTHER_VALUE
79+
}
80+
}
81+
5582
public abstract static class MathPowerTests {
5683
protected static BiFunction<Integer, Integer, Integer> powFunction;
5784

0 commit comments

Comments
 (0)