From ba610978b207a2254dd4593531e7494d2e671a64 Mon Sep 17 00:00:00 2001 From: Christian Stein Date: Tue, 23 Sep 2025 10:14:44 +0200 Subject: [PATCH 1/5] Add `Module` instance support in `ModuleSelector` Closes #4852 --- .../commons/support/ReflectionSupport.java | 25 +++++++++ .../commons/support/ResourceSupport.java | 20 +++++++ .../platform/commons/util/ModuleUtils.java | 54 ++++++++++++++++--- .../commons/util/ReflectionUtils.java | 24 +++++++++ .../engine/discovery/DiscoverySelectors.java | 16 ++++++ .../engine/discovery/ModuleSelector.java | 17 ++++++ .../ClassContainerSelectorResolver.java | 4 ++ .../ResourceContainerSelectorResolver.java | 4 ++ .../support/ReflectionSupportTests.java | 2 +- .../commons/support/ResourceSupportTests.java | 2 +- .../discovery/DiscoverySelectorsTests.java | 18 ++++++- .../engine/discovery/ModuleSelectorTests.java | 11 ++++ 12 files changed, 188 insertions(+), 9 deletions(-) diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/ReflectionSupport.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/ReflectionSupport.java index 28f6d32b2a26..2439ed3b67e6 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/ReflectionSupport.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/ReflectionSupport.java @@ -391,6 +391,31 @@ public static List> findAllClassesInModule(String moduleName, Predicate return ReflectionUtils.findAllClassesInModule(moduleName, classFilter, classNameFilter); } + /** + * Find all {@linkplain Class classes} in the supplied {@code module} + * that match the specified {@code classFilter} and {@code classNameFilter} + * predicates. + * + *

The module-path scanning algorithm searches recursively in all + * packages contained in the module. + * + * @param module the module to scan; never {@code null} or empty + * @param classFilter the class type filter; never {@code null} + * @param classNameFilter the class name filter; never {@code null} + * @return an immutable list of all such classes found; never {@code null} + * but potentially empty + * @since 6.1 + * @see #findAllClassesInClasspathRoot(URI, Predicate, Predicate) + * @see #findAllClassesInPackage(String, Predicate, Predicate) + * @see ResourceSupport#findAllResourcesInModule(String, ResourceFilter) + */ + @API(status = MAINTAINED, since = "6.1") + public static List> findAllClassesInModule(Module module, Predicate> classFilter, + Predicate classNameFilter) { + + return ReflectionUtils.findAllClassesInModule(module, classFilter, classNameFilter); + } + /** * Find all {@linkplain Resource resources} in the supplied {@code moduleName} * that match the specified {@code resourceFilter} predicate. diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/ResourceSupport.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/ResourceSupport.java index 248f6d0b1ab2..b1c5ea4bb3ca 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/ResourceSupport.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/ResourceSupport.java @@ -193,6 +193,26 @@ public static List findAllResourcesInModule(String moduleName, Resourc return ReflectionUtils.findAllResourcesInModule(moduleName, resourceFilter); } + /** + * Find all {@linkplain Resource resources} in the supplied {@code moduleName} + * that match the specified {@code resourceFilter}. + * + *

The module-path scanning algorithm searches recursively in all + * packages contained in the module. + * + * @param module the module to scan; never {@code null} or empty + * @param resourceFilter the resource type filter; never {@code null} + * @return an immutable list of all such resources found; never {@code null} + * but potentially empty + * @see #findAllResourcesInClasspathRoot(URI, ResourceFilter) + * @see #findAllResourcesInPackage(String, ResourceFilter) + * @see ReflectionSupport#findAllClassesInModule(String, Predicate, Predicate) + */ + @API(status = MAINTAINED, since = "6.1") + public static List findAllResourcesInModule(Module module, ResourceFilter resourceFilter) { + return ReflectionUtils.findAllResourcesInModule(module, resourceFilter); + } + /** * Find all {@linkplain Resource resources} in the supplied {@code moduleName} * that match the specified {@code resourceFilter}. diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ModuleUtils.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ModuleUtils.java index a279ac135459..35c2db6821a1 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ModuleUtils.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ModuleUtils.java @@ -109,6 +109,27 @@ public static List> findAllClassesInModule(String moduleName, ClassFilt return scan(moduleReferences, filter, ModuleUtils.class.getClassLoader()); } + /** + * Find all {@linkplain Class classes} for the given module. + * + * @param module the module to scan; never {@code null} or empty + * @param filter the class filter to apply; never {@code null} + * @return an immutable list of all such classes found; never {@code null} + * but potentially empty + * @since 6.1 + */ + @API(status = INTERNAL, since = "6.1") + public static List> findAllClassesInModule(Module module, ClassFilter filter) { + Preconditions.notNull(module, "Module must not be null"); + Preconditions.condition(module.isNamed(), "Module must not be unnamed"); + Preconditions.notNull(filter, "Class filter must not be null"); + + String name = module.getName(); + logger.debug(() -> "Looking for classes in module: " + name); + var reference = module.getLayer().configuration().findModule(name).orElseThrow().reference(); + return scan(Set.of(reference), filter, module.getClassLoader()); + } + /** * Find all {@linkplain Resource resources} for the given module name. * @@ -124,7 +145,7 @@ public static List findAllResourcesInModule(String moduleName, Resourc Preconditions.notBlank(moduleName, "Module name must not be null or empty"); Preconditions.notNull(filter, "Resource filter must not be null"); - logger.debug(() -> "Looking for classes in module: " + moduleName); + logger.debug(() -> "Looking for resources in module: " + moduleName); // @formatter:off Set moduleReferences = streamResolvedModules(isEqual(moduleName)) .map(ResolvedModule::reference) @@ -133,6 +154,27 @@ public static List findAllResourcesInModule(String moduleName, Resourc return scan(moduleReferences, filter, ModuleUtils.class.getClassLoader()); } + /** + * Find all {@linkplain Resource resources} for the given module. + * + * @param module the module to scan; never {@code null} or empty + * @param filter the class filter to apply; never {@code null} + * @return an immutable list of all such resources found; never {@code null} + * but potentially empty + * @since 6.1 + */ + @API(status = INTERNAL, since = "6.1") + public static List findAllResourcesInModule(Module module, ResourceFilter filter) { + Preconditions.notNull(module, "Module must not be null"); + Preconditions.condition(module.isNamed(), "Module must not be unnamed"); + Preconditions.notNull(filter, "Resource filter must not be null"); + + String name = module.getName(); + logger.debug(() -> "Looking for resources in module: " + name); + var reference = module.getLayer().configuration().findModule(name).orElseThrow().reference(); + return scan(Set.of(reference), filter, module.getClassLoader()); + } + /** * Stream resolved modules from current (or boot) module layer. */ @@ -175,18 +217,18 @@ private static List> scan(Set references, ClassFilter } /** - * Scan for classes using the supplied set of module references, class + * Scan for resources using the supplied set of module references, class * filter, and loader. */ private static List scan(Set references, ResourceFilter filter, ClassLoader loader) { logger.debug(() -> "Scanning " + references.size() + " module references: " + references); ModuleReferenceResourceScanner scanner = new ModuleReferenceResourceScanner(filter, loader); - List classes = new ArrayList<>(); + List resources = new ArrayList<>(); for (ModuleReference reference : references) { - classes.addAll(scanner.scan(reference)); + resources.addAll(scanner.scan(reference)); } - logger.debug(() -> "Found " + classes.size() + " classes: " + classes); - return List.copyOf(classes); + logger.debug(() -> "Found " + resources.size() + " resources: " + resources); + return List.copyOf(resources); } /** 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 1ae566fde234..4e47d4c1be0e 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 @@ -1061,6 +1061,16 @@ public static List> findAllClassesInModule(String moduleName, Predicate return findAllClassesInModule(moduleName, ClassFilter.of(classNameFilter, classFilter)); } + /** + * @since 6.1 + * @see org.junit.platform.commons.support.ReflectionSupport#findAllClassesInModule(Module, Predicate, Predicate) + */ + public static List> findAllClassesInModule(Module module, Predicate> classFilter, + Predicate classNameFilter) { + // unmodifiable since returned by public, non-internal method(s) + return findAllClassesInModule(module, ClassFilter.of(classNameFilter, classFilter)); + } + /** * @since 1.10 * @see org.junit.platform.commons.support.ReflectionSupport#streamAllClassesInModule(String, Predicate, Predicate) @@ -1077,6 +1087,13 @@ public static List> findAllClassesInModule(String moduleName, ClassFilt return List.copyOf(ModuleUtils.findAllClassesInModule(moduleName, classFilter)); } + /** + * @since 6.1 + */ + public static List> findAllClassesInModule(Module module, ClassFilter classFilter) { + return List.copyOf(ModuleUtils.findAllClassesInModule(module, classFilter)); + } + /** * @since 1.11 */ @@ -1084,6 +1101,13 @@ public static List findAllResourcesInModule(String moduleName, Resourc return List.copyOf(ModuleUtils.findAllResourcesInModule(moduleName, resourceFilter)); } + /** + * @since 6.1 + */ + public static List findAllResourcesInModule(Module module, ResourceFilter resourceFilter) { + return List.copyOf(ModuleUtils.findAllResourcesInModule(module, resourceFilter)); + } + /** * @since 1.10 */ diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/DiscoverySelectors.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/DiscoverySelectors.java index 29c79114022a..ca5a1f7c2783 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/DiscoverySelectors.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/DiscoverySelectors.java @@ -428,6 +428,22 @@ public static ModuleSelector selectModule(String moduleName) { return new ModuleSelector(moduleName.strip()); } + /** + * Create a {@code ModuleSelector} for the supplied module. + * + *

The unnamed module is not supported. + * + * @param module the module to select; never {@code null} or blank + * @since 6.1 + * @see ModuleSelector + */ + @API(status = STABLE, since = "6.1") + public static ModuleSelector selectModule(Module module) { + Preconditions.notNull(module, "Module must not be null"); + Preconditions.condition(module.isNamed(), "Module must be named"); + return new ModuleSelector(module); + } + /** * Create a list of {@code ModuleSelectors} for the supplied module names. * diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/ModuleSelector.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/ModuleSelector.java index 1ecd5d7de8fa..5a87bc5fe909 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/ModuleSelector.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/ModuleSelector.java @@ -17,6 +17,7 @@ import java.util.Optional; import org.apiguardian.api.API; +import org.jspecify.annotations.Nullable; import org.junit.platform.commons.util.ToStringBuilder; import org.junit.platform.engine.DiscoverySelector; import org.junit.platform.engine.DiscoverySelectorIdentifier; @@ -33,12 +34,28 @@ @API(status = STABLE, since = "1.1") public final class ModuleSelector implements DiscoverySelector { + @Nullable + private final Module module; private final String moduleName; + ModuleSelector(Module module) { + this.module = module; + this.moduleName = module.getName(); + } + ModuleSelector(String moduleName) { + this.module = null; this.moduleName = moduleName; } + /** + * @return the selected module wrapped in an {@link Optional}. + */ + @API(status = STABLE, since = "6.1") + public Optional getModule() { + return Optional.ofNullable(module); + } + /** * Get the selected module name. */ diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/discovery/ClassContainerSelectorResolver.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/discovery/ClassContainerSelectorResolver.java index 4dfae26276f5..988ceafa7f6d 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/discovery/ClassContainerSelectorResolver.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/discovery/ClassContainerSelectorResolver.java @@ -46,6 +46,10 @@ public Resolution resolve(ClasspathRootSelector selector, Context context) { @Override public Resolution resolve(ModuleSelector selector, Context context) { + if (selector.getModule().isPresent()) { + Module module = selector.getModule().get(); + return classSelectors(findAllClassesInModule(module, classFilter, classNameFilter)); + } return classSelectors(findAllClassesInModule(selector.getModuleName(), classFilter, classNameFilter)); } diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/discovery/ResourceContainerSelectorResolver.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/discovery/ResourceContainerSelectorResolver.java index a108bee73414..507b4fb32c50 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/support/discovery/ResourceContainerSelectorResolver.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/support/discovery/ResourceContainerSelectorResolver.java @@ -49,6 +49,10 @@ public Resolution resolve(ClasspathRootSelector selector, Context context) { @Override public Resolution resolve(ModuleSelector selector, Context context) { + if (selector.getModule().isPresent()) { + Module module = selector.getModule().get(); + return resourceSelectors(findAllResourcesInModule(module, resourceFilter)); + } return resourceSelectors(findAllResourcesInModule(selector.getModuleName(), resourceFilter)); } diff --git a/platform-tests/src/test/java/org/junit/platform/commons/support/ReflectionSupportTests.java b/platform-tests/src/test/java/org/junit/platform/commons/support/ReflectionSupportTests.java index 1b9149ce956a..ce9ce223a634 100644 --- a/platform-tests/src/test/java/org/junit/platform/commons/support/ReflectionSupportTests.java +++ b/platform-tests/src/test/java/org/junit/platform/commons/support/ReflectionSupportTests.java @@ -299,7 +299,7 @@ void findAllClassesInModuleDelegates() { @Test void findAllClassesInModulePreconditions() { assertPreconditionViolationNotNullOrEmptyFor("Module name", - () -> ReflectionSupport.findAllClassesInModule(null, allTypes, allNames)); + () -> ReflectionSupport.findAllClassesInModule((String) null, allTypes, allNames)); assertPreconditionViolationNotNullFor("class predicate", () -> ReflectionSupport.findAllClassesInModule("org.junit.platform.commons", null, allNames)); assertPreconditionViolationNotNullFor("name predicate", diff --git a/platform-tests/src/test/java/org/junit/platform/commons/support/ResourceSupportTests.java b/platform-tests/src/test/java/org/junit/platform/commons/support/ResourceSupportTests.java index 0cc207eab838..c1acc3d21e76 100644 --- a/platform-tests/src/test/java/org/junit/platform/commons/support/ResourceSupportTests.java +++ b/platform-tests/src/test/java/org/junit/platform/commons/support/ResourceSupportTests.java @@ -185,7 +185,7 @@ void findAllResourcesInModuleDelegates() { @Test void findAllResourcesInModulePreconditions() { assertPreconditionViolationNotNullOrEmptyFor("Module name", - () -> ResourceSupport.findAllResourcesInModule(null, allResources)); + () -> ResourceSupport.findAllResourcesInModule((String) null, allResources)); assertPreconditionViolationNotNullFor("Resource filter", () -> ResourceSupport.findAllResourcesInModule("org.junit.platform.commons", null)); } diff --git a/platform-tests/src/test/java/org/junit/platform/engine/discovery/DiscoverySelectorsTests.java b/platform-tests/src/test/java/org/junit/platform/engine/discovery/DiscoverySelectorsTests.java index 83126ede3cfa..ca7065ad17ad 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/discovery/DiscoverySelectorsTests.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/discovery/DiscoverySelectorsTests.java @@ -16,6 +16,7 @@ import static org.assertj.core.api.InstanceOfAssertFactories.type; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.engine.discovery.JupiterUniqueIdBuilder.uniqueIdForMethod; import static org.junit.jupiter.params.provider.Arguments.arguments; import static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor; @@ -422,7 +423,7 @@ class SelectModuleTests { @SuppressWarnings("DataFlowIssue") @Test void selectModuleByNamePreconditions() { - assertPreconditionViolationFor(() -> selectModule(null)); + assertPreconditionViolationFor(() -> selectModule((String) null)); assertPreconditionViolationFor(() -> selectModule("")); assertPreconditionViolationFor(() -> selectModule(" ")); } @@ -433,6 +434,21 @@ void selectModuleByName() { assertEquals("java.base", selector.getModuleName()); } + @SuppressWarnings("DataFlowIssue") + @Test + void selectModuleByInstancePreconditions() { + assertPreconditionViolationFor(() -> selectModule((Module) null)); + assertPreconditionViolationFor(() -> selectModule(getClass().getClassLoader().getUnnamedModule())); + } + + @Test + void selectModuleByInstance() { + var module = Object.class.getModule(); + var selector = selectModule(module); + assertEquals("java.base", selector.getModuleName()); + assertSame(module, selector.getModule().orElseThrow()); + } + @SuppressWarnings("DataFlowIssue") @Test void selectModulesByNamesPreconditions() { diff --git a/platform-tests/src/test/java/org/junit/platform/engine/discovery/ModuleSelectorTests.java b/platform-tests/src/test/java/org/junit/platform/engine/discovery/ModuleSelectorTests.java index 34cf92a2c4db..532e83f31e20 100644 --- a/platform-tests/src/test/java/org/junit/platform/engine/discovery/ModuleSelectorTests.java +++ b/platform-tests/src/test/java/org/junit/platform/engine/discovery/ModuleSelectorTests.java @@ -12,6 +12,8 @@ import static org.junit.jupiter.api.EqualsAndHashCodeAssertions.assertEqualsAndHashCode; +import java.util.logging.Logger; + import org.junit.jupiter.api.Test; /** @@ -31,4 +33,13 @@ void equalsAndHashCode() { assertEqualsAndHashCode(selector1, selector2, selector3); } + @Test + void equalsAndHashCodeForModuleInstances() { + var selector1 = new ModuleSelector(Object.class.getModule()); // java.base + var selector2 = new ModuleSelector(Object.class.getModule()); // java.base + var selector3 = new ModuleSelector(Logger.class.getModule()); // java.logging + + assertEqualsAndHashCode(selector1, selector2, selector3); + } + } From e0cbd04e36ae30ef6ef2ec1d2bb64604053a090a Mon Sep 17 00:00:00 2001 From: Christian Stein Date: Tue, 7 Oct 2025 10:36:04 +0200 Subject: [PATCH 2/5] Add release note --- .../docs/asciidoc/release-notes/release-notes-6.1.0-M1.adoc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-6.1.0-M1.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-6.1.0-M1.adoc index d255eb41044b..04e694f1d545 100644 --- a/documentation/src/docs/asciidoc/release-notes/release-notes-6.1.0-M1.adoc +++ b/documentation/src/docs/asciidoc/release-notes/release-notes-6.1.0-M1.adoc @@ -26,7 +26,8 @@ repository on GitHub. [[release-notes-6.1.0-M1-junit-platform-new-features-and-improvements]] ==== New Features and Improvements -* ❓ +* Support for creating a `ModuleSelector` from a `java.lang.Module` and using + its classloader for test discovery. [[release-notes-6.1.0-M1-junit-jupiter]] From 8ff80957a6a350026b26c81392010aa2927a3a34 Mon Sep 17 00:00:00 2001 From: Christian Stein Date: Tue, 7 Oct 2025 17:04:13 +0200 Subject: [PATCH 3/5] Apply review suggestions --- .../junit/platform/commons/support/ReflectionSupport.java | 5 +++-- .../junit/platform/commons/support/ResourceSupport.java | 8 +++++--- .../java/org/junit/platform/commons/util/ModuleUtils.java | 2 +- .../platform/engine/discovery/DiscoverySelectors.java | 4 ++-- .../junit/platform/engine/discovery/ModuleSelector.java | 8 ++++++-- .../platform/commons/support/ReflectionSupportTests.java | 2 ++ .../platform/commons/support/ResourceSupportTests.java | 2 ++ 7 files changed, 21 insertions(+), 10 deletions(-) diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/ReflectionSupport.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/ReflectionSupport.java index 2439ed3b67e6..0501f8211e39 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/ReflectionSupport.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/ReflectionSupport.java @@ -10,6 +10,7 @@ package org.junit.platform.commons.support; +import static org.apiguardian.api.API.Status.EXPERIMENTAL; import static org.apiguardian.api.API.Status.DEPRECATED; import static org.apiguardian.api.API.Status.MAINTAINED; @@ -399,7 +400,7 @@ public static List> findAllClassesInModule(String moduleName, Predicate *

The module-path scanning algorithm searches recursively in all * packages contained in the module. * - * @param module the module to scan; never {@code null} or empty + * @param module the module to scan; never {@code null} or unnamed * @param classFilter the class type filter; never {@code null} * @param classNameFilter the class name filter; never {@code null} * @return an immutable list of all such classes found; never {@code null} @@ -409,7 +410,7 @@ public static List> findAllClassesInModule(String moduleName, Predicate * @see #findAllClassesInPackage(String, Predicate, Predicate) * @see ResourceSupport#findAllResourcesInModule(String, ResourceFilter) */ - @API(status = MAINTAINED, since = "6.1") + @API(status = EXPERIMENTAL, since = "6.1") public static List> findAllClassesInModule(Module module, Predicate> classFilter, Predicate classNameFilter) { diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/ResourceSupport.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/ResourceSupport.java index b1c5ea4bb3ca..5a4b32130030 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/ResourceSupport.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/ResourceSupport.java @@ -10,6 +10,7 @@ package org.junit.platform.commons.support; +import static org.apiguardian.api.API.Status.EXPERIMENTAL; import static org.apiguardian.api.API.Status.MAINTAINED; import java.net.URI; @@ -194,21 +195,22 @@ public static List findAllResourcesInModule(String moduleName, Resourc } /** - * Find all {@linkplain Resource resources} in the supplied {@code moduleName} + * Find all {@linkplain Resource resources} in the supplied {@code module} * that match the specified {@code resourceFilter}. * *

The module-path scanning algorithm searches recursively in all * packages contained in the module. * - * @param module the module to scan; never {@code null} or empty + * @param module the module to scan; never {@code null} or unnamed * @param resourceFilter the resource type filter; never {@code null} * @return an immutable list of all such resources found; never {@code null} * but potentially empty + * @since 6.1 * @see #findAllResourcesInClasspathRoot(URI, ResourceFilter) * @see #findAllResourcesInPackage(String, ResourceFilter) * @see ReflectionSupport#findAllClassesInModule(String, Predicate, Predicate) */ - @API(status = MAINTAINED, since = "6.1") + @API(status = EXPERIMENTAL, since = "6.1") public static List findAllResourcesInModule(Module module, ResourceFilter resourceFilter) { return ReflectionUtils.findAllResourcesInModule(module, resourceFilter); } diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ModuleUtils.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ModuleUtils.java index 35c2db6821a1..9c0ae00120c4 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ModuleUtils.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ModuleUtils.java @@ -112,7 +112,7 @@ public static List> findAllClassesInModule(String moduleName, ClassFilt /** * Find all {@linkplain Class classes} for the given module. * - * @param module the module to scan; never {@code null} or empty + * @param module the module to scan; never {@code null} or unnamed * @param filter the class filter to apply; never {@code null} * @return an immutable list of all such classes found; never {@code null} * but potentially empty diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/DiscoverySelectors.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/DiscoverySelectors.java index ca5a1f7c2783..137a6507c8eb 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/DiscoverySelectors.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/DiscoverySelectors.java @@ -433,11 +433,11 @@ public static ModuleSelector selectModule(String moduleName) { * *

The unnamed module is not supported. * - * @param module the module to select; never {@code null} or blank + * @param module the module to select; never {@code null} or unnamed * @since 6.1 * @see ModuleSelector */ - @API(status = STABLE, since = "6.1") + @API(status = EXPERIMENTAL, since = "6.1") public static ModuleSelector selectModule(Module module) { Preconditions.notNull(module, "Module must not be null"); Preconditions.condition(module.isNamed(), "Module must be named"); diff --git a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/ModuleSelector.java b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/ModuleSelector.java index 5a87bc5fe909..2d8958b23e91 100644 --- a/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/ModuleSelector.java +++ b/junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/ModuleSelector.java @@ -10,6 +10,7 @@ package org.junit.platform.engine.discovery; +import static org.apiguardian.api.API.Status.EXPERIMENTAL; import static org.apiguardian.api.API.Status.INTERNAL; import static org.apiguardian.api.API.Status.STABLE; @@ -49,9 +50,12 @@ public final class ModuleSelector implements DiscoverySelector { } /** - * @return the selected module wrapped in an {@link Optional}. + * {@return the selected {@link Module}, if available} + * + * @since 6.1 + * @see DiscoverySelectors#selectModule(Module) */ - @API(status = STABLE, since = "6.1") + @API(status = EXPERIMENTAL, since = "6.1") public Optional getModule() { return Optional.ofNullable(module); } diff --git a/platform-tests/src/test/java/org/junit/platform/commons/support/ReflectionSupportTests.java b/platform-tests/src/test/java/org/junit/platform/commons/support/ReflectionSupportTests.java index ce9ce223a634..19f5524e8ed5 100644 --- a/platform-tests/src/test/java/org/junit/platform/commons/support/ReflectionSupportTests.java +++ b/platform-tests/src/test/java/org/junit/platform/commons/support/ReflectionSupportTests.java @@ -300,6 +300,8 @@ void findAllClassesInModuleDelegates() { void findAllClassesInModulePreconditions() { assertPreconditionViolationNotNullOrEmptyFor("Module name", () -> ReflectionSupport.findAllClassesInModule((String) null, allTypes, allNames)); + assertPreconditionViolationNotNullOrEmptyFor("Module instance", + () -> ReflectionSupport.findAllClassesInModule((Module) null, allTypes, allNames)); assertPreconditionViolationNotNullFor("class predicate", () -> ReflectionSupport.findAllClassesInModule("org.junit.platform.commons", null, allNames)); assertPreconditionViolationNotNullFor("name predicate", diff --git a/platform-tests/src/test/java/org/junit/platform/commons/support/ResourceSupportTests.java b/platform-tests/src/test/java/org/junit/platform/commons/support/ResourceSupportTests.java index c1acc3d21e76..d4bf811f996b 100644 --- a/platform-tests/src/test/java/org/junit/platform/commons/support/ResourceSupportTests.java +++ b/platform-tests/src/test/java/org/junit/platform/commons/support/ResourceSupportTests.java @@ -186,6 +186,8 @@ void findAllResourcesInModuleDelegates() { void findAllResourcesInModulePreconditions() { assertPreconditionViolationNotNullOrEmptyFor("Module name", () -> ResourceSupport.findAllResourcesInModule((String) null, allResources)); + assertPreconditionViolationNotNullOrEmptyFor("Module instance", + () -> ResourceSupport.findAllResourcesInModule((Module) null, allResources)); assertPreconditionViolationNotNullFor("Resource filter", () -> ResourceSupport.findAllResourcesInModule("org.junit.platform.commons", null)); } From 77abe84efd4a3bbd8dea45ab887d40b60de712a0 Mon Sep 17 00:00:00 2001 From: Christian Stein Date: Tue, 7 Oct 2025 17:25:08 +0200 Subject: [PATCH 4/5] Apply spotless --- .../org/junit/platform/commons/support/ReflectionSupport.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/ReflectionSupport.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/ReflectionSupport.java index 0501f8211e39..23bbc51d8665 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/ReflectionSupport.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/ReflectionSupport.java @@ -10,8 +10,8 @@ package org.junit.platform.commons.support; -import static org.apiguardian.api.API.Status.EXPERIMENTAL; import static org.apiguardian.api.API.Status.DEPRECATED; +import static org.apiguardian.api.API.Status.EXPERIMENTAL; import static org.apiguardian.api.API.Status.MAINTAINED; import java.lang.reflect.Field; From 8f906119d12a3d20846ff893bac9712007b506b5 Mon Sep 17 00:00:00 2001 From: Christian Stein Date: Tue, 7 Oct 2025 17:42:33 +0200 Subject: [PATCH 5/5] Use correct assertion helpers --- .../junit/platform/commons/support/ReflectionSupportTests.java | 2 +- .../junit/platform/commons/support/ResourceSupportTests.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/platform-tests/src/test/java/org/junit/platform/commons/support/ReflectionSupportTests.java b/platform-tests/src/test/java/org/junit/platform/commons/support/ReflectionSupportTests.java index 19f5524e8ed5..d094d5e0a881 100644 --- a/platform-tests/src/test/java/org/junit/platform/commons/support/ReflectionSupportTests.java +++ b/platform-tests/src/test/java/org/junit/platform/commons/support/ReflectionSupportTests.java @@ -300,7 +300,7 @@ void findAllClassesInModuleDelegates() { void findAllClassesInModulePreconditions() { assertPreconditionViolationNotNullOrEmptyFor("Module name", () -> ReflectionSupport.findAllClassesInModule((String) null, allTypes, allNames)); - assertPreconditionViolationNotNullOrEmptyFor("Module instance", + assertPreconditionViolationNotNullFor("Module", () -> ReflectionSupport.findAllClassesInModule((Module) null, allTypes, allNames)); assertPreconditionViolationNotNullFor("class predicate", () -> ReflectionSupport.findAllClassesInModule("org.junit.platform.commons", null, allNames)); diff --git a/platform-tests/src/test/java/org/junit/platform/commons/support/ResourceSupportTests.java b/platform-tests/src/test/java/org/junit/platform/commons/support/ResourceSupportTests.java index d4bf811f996b..35d5648c66b8 100644 --- a/platform-tests/src/test/java/org/junit/platform/commons/support/ResourceSupportTests.java +++ b/platform-tests/src/test/java/org/junit/platform/commons/support/ResourceSupportTests.java @@ -186,7 +186,7 @@ void findAllResourcesInModuleDelegates() { void findAllResourcesInModulePreconditions() { assertPreconditionViolationNotNullOrEmptyFor("Module name", () -> ResourceSupport.findAllResourcesInModule((String) null, allResources)); - assertPreconditionViolationNotNullOrEmptyFor("Module instance", + assertPreconditionViolationNotNullFor("Module", () -> ResourceSupport.findAllResourcesInModule((Module) null, allResources)); assertPreconditionViolationNotNullFor("Resource filter", () -> ResourceSupport.findAllResourcesInModule("org.junit.platform.commons", null));