Skip to content

Commit a30437b

Browse files
committed
Support explicit ClassLoader in NestedClassSelector
Issue: #3298
1 parent e576cff commit a30437b

File tree

5 files changed

+82
-16
lines changed

5 files changed

+82
-16
lines changed

documentation/src/docs/asciidoc/release-notes/release-notes-5.10.0-M1.adoc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@ repository on GitHub.
3737
`ClassLoader`. This allows parameter types to be resolved with custom `ClassLoader`
3838
arrangements (such as OSGi). Consequently, `DiscoverySelectors.selectMethod(Class<?>,
3939
String, String)` also now works properly with custom `ClassLoader` arrangements.
40-
* New overloaded constructors for `ClassSelector` and `MethodSelector` that take an
41-
explicit `ClassLoader` as a parameter, allowing selectors to select classes in custom
42-
`ClassLoader` arrangements like in OSGi.
40+
* New overloaded constructors for `ClassSelector`, `NestedClassSelector`, and
41+
`MethodSelector` that take an explicit `ClassLoader` as a parameter, allowing selectors
42+
to select classes in custom `ClassLoader` arrangements like in OSGi.
4343
* For consistency with JUnit Jupiter lifecycle callbacks, listener method pairs for
4444
started/finished and opened/closed events are now invoked using "wrapping" semantics.
4545
This means that finished/closed event methods are invoked in reverse order compared to

junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/DiscoverySelectors.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -635,9 +635,26 @@ public static NestedClassSelector selectNestedClass(List<Class<?>> enclosingClas
635635
*/
636636
@API(status = STABLE, since = "1.6")
637637
public static NestedClassSelector selectNestedClass(List<String> enclosingClassNames, String nestedClassName) {
638+
return selectNestedClass(enclosingClassNames, nestedClassName, null);
639+
}
640+
641+
/**
642+
* Create a {@code NestedClassSelector} for the supplied class name, its enclosing
643+
* classes' names, and class loader.
644+
*
645+
* @param enclosingClassNames the names of the enclosing classes; never {@code null} or empty
646+
* @param nestedClassName the name of the nested class to select; never {@code null} or blank
647+
* @param classLoader the class loader to use to load the enclosing and nested classes, or
648+
* {@code null} to signal that the default {@code ClassLoader} should be used
649+
* @since 1.10
650+
* @see NestedClassSelector
651+
*/
652+
@API(status = EXPERIMENTAL, since = "1.10")
653+
public static NestedClassSelector selectNestedClass(List<String> enclosingClassNames, String nestedClassName,
654+
ClassLoader classLoader) {
638655
Preconditions.notEmpty(enclosingClassNames, "Enclosing class names must not be null or empty");
639656
Preconditions.notBlank(nestedClassName, "Nested class name must not be null or blank");
640-
return new NestedClassSelector(enclosingClassNames, nestedClassName);
657+
return new NestedClassSelector(enclosingClassNames, nestedClassName, classLoader);
641658
}
642659

643660
/**

junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/NestedClassSelector.java

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
package org.junit.platform.engine.discovery;
1212

1313
import static java.util.stream.Collectors.toList;
14+
import static org.apiguardian.api.API.Status.EXPERIMENTAL;
1415
import static org.apiguardian.api.API.Status.STABLE;
16+
import static org.junit.platform.commons.util.CollectionUtils.toUnmodifiableList;
1517

1618
import java.util.List;
1719
import java.util.Objects;
@@ -46,17 +48,31 @@
4648
@API(status = STABLE, since = "1.6")
4749
public class NestedClassSelector implements DiscoverySelector {
4850

49-
private List<ClassSelector> enclosingClassSelectors;
50-
private ClassSelector nestedClassSelector;
51+
private final List<ClassSelector> enclosingClassSelectors;
52+
private final ClassSelector nestedClassSelector;
53+
private final ClassLoader classLoader;
5154

52-
NestedClassSelector(List<String> enclosingClassNames, String nestedClassName) {
53-
this.enclosingClassSelectors = enclosingClassNames.stream().map(ClassSelector::new).collect(toList());
54-
this.nestedClassSelector = new ClassSelector(nestedClassName);
55+
NestedClassSelector(List<String> enclosingClassNames, String nestedClassName, ClassLoader classLoader) {
56+
this.enclosingClassSelectors = enclosingClassNames.stream() //
57+
.map(className -> new ClassSelector(className, classLoader)) //
58+
.collect(toUnmodifiableList());
59+
this.nestedClassSelector = new ClassSelector(nestedClassName, classLoader);
60+
this.classLoader = classLoader;
5561
}
5662

5763
NestedClassSelector(List<Class<?>> enclosingClasses, Class<?> nestedClass) {
5864
this.enclosingClassSelectors = enclosingClasses.stream().map(ClassSelector::new).collect(toList());
5965
this.nestedClassSelector = new ClassSelector(nestedClass);
66+
this.classLoader = nestedClass.getClassLoader();
67+
}
68+
69+
/**
70+
* Get the {@link ClassLoader} used to load the selected nested class.
71+
* @since 1.10
72+
*/
73+
@API(status = EXPERIMENTAL, since = "1.10")
74+
public ClassLoader getClassLoader() {
75+
return this.classLoader;
6076
}
6177

6278
/**
@@ -121,6 +137,7 @@ public String toString() {
121137
return new ToStringBuilder(this) //
122138
.append("enclosingClassNames", getEnclosingClassNames()) //
123139
.append("nestedClassName", getNestedClassName()) //
140+
.append("classLoader", getClassLoader()) //
124141
.toString();
125142
}
126143

platform-tests/src/test/java/org/junit/platform/engine/discovery/DiscoverySelectorsTests.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -691,6 +691,27 @@ void selectNestedClassByClassNames() {
691691
assertThat(selector.getNestedClassName()).isEqualTo(nestedClassName);
692692
}
693693

694+
@Test
695+
void selectNestedClassByClassNamesAndClassLoader() throws Exception {
696+
var testClasses = List.of(AbstractClassWithNestedInnerClass.class, ClassWithNestedInnerClass.class,
697+
AbstractClassWithNestedInnerClass.NestedClass.class);
698+
try (var testClassLoader = TestClassLoader.forClasses(testClasses.toArray(Class[]::new))) {
699+
700+
var selector = selectNestedClass(List.of(enclosingClassName), nestedClassName, testClassLoader);
701+
702+
assertThat(selector.getEnclosingClasses()).doesNotContain(ClassWithNestedInnerClass.class);
703+
assertThat(selector.getEnclosingClasses()).extracting(Class::getName).containsOnly(enclosingClassName);
704+
assertThat(selector.getNestedClass()).isNotEqualTo(AbstractClassWithNestedInnerClass.NestedClass.class);
705+
assertThat(selector.getNestedClass().getName()).isEqualTo(nestedClassName);
706+
707+
assertThat(selector.getClassLoader()).isSameAs(testClassLoader);
708+
assertThat(selector.getEnclosingClasses()).extracting(Class::getClassLoader).containsOnly(
709+
testClassLoader);
710+
assertThat(selector.getNestedClass().getClassLoader()).isSameAs(testClassLoader);
711+
assertSame(testClassLoader, selector.getNestedClass().getClassLoader());
712+
}
713+
}
714+
694715
@Test
695716
void selectDoubleNestedClassByClassNames() {
696717
var selector = selectNestedClass(List.of(enclosingClassName, nestedClassName), doubleNestedClassName);

platform-tests/src/test/java/org/junit/platform/engine/discovery/NestedClassSelectorTests.java

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,18 +30,18 @@ class NestedClassSelectorTests extends AbstractEqualsAndHashCodeTests {
3030
@Test
3131
void equalsAndHashCode() {
3232
var selector1 = new NestedClassSelector(List.of("org.example.EnclosingTestClass"),
33-
"org.example.NestedTestClass");
33+
"org.example.NestedTestClass", null);
3434
var selector2 = new NestedClassSelector(List.of("org.example.EnclosingTestClass"),
35-
"org.example.NestedTestClass");
36-
var selector3 = new NestedClassSelector(List.of("org.example.X"), "org.example.Y");
35+
"org.example.NestedTestClass", null);
36+
var selector3 = new NestedClassSelector(List.of("org.example.X"), "org.example.Y", null);
3737

3838
assertEqualsAndHashCode(selector1, selector2, selector3);
3939
}
4040

4141
@Test
4242
void preservesOriginalExceptionWhenTryingToLoadEnclosingClasses() {
43-
var selector = new NestedClassSelector(List.of("org.example.EnclosingTestClass"),
44-
"org.example.NestedTestClass");
43+
var selector = new NestedClassSelector(List.of("org.example.EnclosingTestClass"), "org.example.NestedTestClass",
44+
null);
4545

4646
var exception = assertThrows(PreconditionViolationException.class, selector::getEnclosingClasses);
4747

@@ -51,12 +51,23 @@ void preservesOriginalExceptionWhenTryingToLoadEnclosingClasses() {
5151

5252
@Test
5353
void preservesOriginalExceptionWhenTryingToLoadNestedClass() {
54-
var selector = new NestedClassSelector(List.of("org.example.EnclosingTestClass"),
55-
"org.example.NestedTestClass");
54+
var selector = new NestedClassSelector(List.of("org.example.EnclosingTestClass"), "org.example.NestedTestClass",
55+
null);
5656

5757
var exception = assertThrows(PreconditionViolationException.class, selector::getNestedClass);
5858

5959
assertThat(exception).hasMessage("Could not load class with name: org.example.NestedTestClass") //
6060
.hasCauseInstanceOf(ClassNotFoundException.class);
6161
}
62+
63+
@Test
64+
void usesClassClassLoader() {
65+
var selector = new NestedClassSelector(List.of(getClass()), NestedTestCase.class);
66+
67+
assertThat(selector.getClassLoader()).isNotNull().isSameAs(getClass().getClassLoader());
68+
}
69+
70+
@SuppressWarnings("InnerClassMayBeStatic")
71+
class NestedTestCase {
72+
}
6273
}

0 commit comments

Comments
 (0)