-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Part I - Introduce ModuleUtils with ClassFinder SPI #1061
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
/* | ||
* Copyright 2015-2017 the original author or authors. | ||
* | ||
* All rights reserved. This program and the accompanying materials are | ||
* made available under the terms of the Eclipse Public License v2.0 which | ||
* accompanies this distribution and is available at | ||
* | ||
* http://www.eclipse.org/legal/epl-v20.html | ||
*/ | ||
|
||
package org.junit.platform.commons.util; | ||
|
||
import static org.apiguardian.api.API.Status.INTERNAL; | ||
|
||
import java.util.function.Predicate; | ||
|
||
import org.apiguardian.api.API; | ||
|
||
/** | ||
* Scan-able class predicate holder used by reflection utilities. | ||
* | ||
* <h3>DISCLAIMER</h3> | ||
* | ||
* <p>These utilities are intended solely for usage within the JUnit framework | ||
* itself. <strong>Any usage by external parties is not supported.</strong> | ||
* Use at your own risk! | ||
* | ||
* @since 1.1 | ||
*/ | ||
@API(status = INTERNAL, since = "1.1") | ||
public class ClassFilter { | ||
|
||
public static ClassFilter of(Predicate<Class<?>> classPredicate, Predicate<String> namePredicate) { | ||
Preconditions.notNull(classPredicate, "class predicate must not be null"); | ||
Preconditions.notNull(namePredicate, "name predicate must not be null"); | ||
|
||
return new ClassFilter(classPredicate, namePredicate); | ||
} | ||
|
||
private final Predicate<Class<?>> classPredicate; | ||
private final Predicate<String> namePredicate; | ||
|
||
private ClassFilter(Predicate<Class<?>> classPredicate, Predicate<String> namePredicate) { | ||
this.classPredicate = classPredicate; | ||
this.namePredicate = namePredicate; | ||
} | ||
|
||
public boolean test(Class<?> type) { | ||
return classPredicate.test(type); | ||
} | ||
|
||
public boolean test(String name) { | ||
return namePredicate.test(name); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
/* | ||
* Copyright 2015-2017 the original author or authors. | ||
* | ||
* All rights reserved. This program and the accompanying materials are | ||
* made available under the terms of the Eclipse Public License v2.0 which | ||
* accompanies this distribution and is available at | ||
* | ||
* http://www.eclipse.org/legal/epl-v20.html | ||
*/ | ||
|
||
package org.junit.platform.commons.util; | ||
|
||
import static org.apiguardian.api.API.Status.INTERNAL; | ||
|
||
import java.nio.file.Path; | ||
import java.util.List; | ||
|
||
import org.apiguardian.api.API; | ||
|
||
/** | ||
* Class finder service providing interface. | ||
* | ||
* <h3>DISCLAIMER</h3> | ||
* | ||
* <p>These utilities are intended solely for usage within the JUnit framework | ||
* itself. <strong>Any usage by external parties is not supported.</strong> | ||
* Use at your own risk! | ||
* | ||
* @since 1.1 | ||
*/ | ||
@API(status = INTERNAL, since = "1.1") | ||
public interface ModuleClassFinder { | ||
|
||
/** | ||
* Return list of classes found in the named module that may contain testable methods. | ||
* | ||
* @param filter filter to apply to each class candidate | ||
* @param moduleName name of the module to inspect | ||
* @return list of test classes | ||
*/ | ||
List<Class<?>> findAllClassesInModule(ClassFilter filter, String moduleName); | ||
|
||
/** | ||
* Return list of classes found on the boot module-path that may contain testable methods. | ||
* | ||
* @param filter filter to apply to each class candidate | ||
* @return list of test classes | ||
*/ | ||
List<Class<?>> findAllClassesOnModulePath(ClassFilter filter); | ||
|
||
/** | ||
* Return list of classes found at the given path roots that may contain testable methods. | ||
* | ||
* @param filter filter to apply to each class candidate | ||
* @param parent class loader instance to used the parent class loader | ||
* @param entries a possibly-empty array of paths to directories of modules or paths to packaged or exploded modules | ||
* @return list of test classes | ||
* @see <a href="http://download.java.net/java/jdk9/docs/api/java/lang/module/ModuleFinder.html">ModuleFinder</a> | ||
*/ | ||
List<Class<?>> findAllClassesOnModulePath(ClassFilter filter, ClassLoader parent, Path... entries); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
/* | ||
* Copyright 2015-2017 the original author or authors. | ||
* | ||
* All rights reserved. This program and the accompanying materials are | ||
* made available under the terms of the Eclipse Public License v2.0 which | ||
* accompanies this distribution and is available at | ||
* | ||
* http://www.eclipse.org/legal/epl-v20.html | ||
*/ | ||
|
||
package org.junit.platform.commons.util; | ||
|
||
import static org.apiguardian.api.API.Status.INTERNAL; | ||
|
||
import java.nio.file.Path; | ||
import java.util.ArrayList; | ||
import java.util.Collections; | ||
import java.util.List; | ||
import java.util.ServiceLoader; | ||
import java.util.function.Function; | ||
|
||
import org.apiguardian.api.API; | ||
import org.junit.platform.commons.logging.Logger; | ||
import org.junit.platform.commons.logging.LoggerFactory; | ||
|
||
/** | ||
* Collection of utilities for working with modules. | ||
* | ||
* <h3>DISCLAIMER</h3> | ||
* | ||
* <p>These utilities are intended solely for usage within the JUnit framework | ||
* itself. <strong>Any usage by external parties is not supported.</strong> | ||
* Use at your own risk! | ||
* | ||
* @since 1.1 | ||
*/ | ||
@API(status = INTERNAL, since = "1.1") | ||
public final class ModuleUtils { | ||
|
||
private static final Logger logger = LoggerFactory.getLogger(ModuleUtils.class); | ||
|
||
///CLOVER:OFF | ||
private ModuleUtils() { | ||
/* no-op */ | ||
} | ||
///CLOVER:ON | ||
|
||
/** | ||
* Find all classes in the specified module. | ||
* | ||
* @param filter filter to apply to each candidate | ||
* @param moduleName name of the module to inspect | ||
* @return list of classes matching the passed-in criteria | ||
*/ | ||
public static List<Class<?>> findAllClassesInModule(ClassFilter filter, String moduleName) { | ||
Preconditions.notNull(filter, "class filter must not be null"); | ||
Preconditions.notBlank(moduleName, "module name must not be null or blank"); | ||
|
||
return find(finder -> finder.findAllClassesInModule(filter, moduleName)); | ||
} | ||
|
||
/** | ||
* Find all classes in all modules that are on the boot module-path. | ||
* | ||
* @param filter filter to apply to each candidate | ||
* @return list of classes matching the passed-in criteria | ||
*/ | ||
public static List<Class<?>> findAllClassesOnModulePath(ClassFilter filter) { | ||
Preconditions.notNull(filter, "class filter must not be null"); | ||
|
||
return find(finder -> finder.findAllClassesOnModulePath(filter)); | ||
} | ||
|
||
/** | ||
* Find all classes in all modules that are locatable on the given path entries. | ||
* | ||
* @param filter filter to apply to each candidate | ||
* @param parent class loader parent | ||
* @return list of classes matching the passed-in criteria | ||
*/ | ||
public static List<Class<?>> findAllClassesOnModulePath(ClassFilter filter, ClassLoader parent, Path... entries) { | ||
Preconditions.notNull(filter, "class filter must not be null"); | ||
Preconditions.notNull(filter, "class loader must not be null"); | ||
|
||
return find(finder -> finder.findAllClassesOnModulePath(filter, parent, entries)); | ||
} | ||
|
||
private static List<Class<?>> find(Function<ModuleClassFinder, List<Class<?>>> function) { | ||
ClassLoader classLoader = ClassLoaderUtils.getDefaultClassLoader(); | ||
List<Class<?>> classes = new ArrayList<>(); | ||
|
||
logger.config(() -> "Loading auto-detected class finders..."); | ||
int serviceCounter = 0; | ||
for (ModuleClassFinder classFinder : ServiceLoader.load(ModuleClassFinder.class, classLoader)) { | ||
classes.addAll(function.apply(classFinder)); | ||
serviceCounter++; | ||
} | ||
if (serviceCounter == 0) { | ||
logger.warn(() -> "No module class finder service registered! No test classes found."); | ||
} | ||
return Collections.unmodifiableList(classes); | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -50,6 +50,8 @@ class AvailableOptions { | |
private final OptionSpec<URI> selectedUris; | ||
private final OptionSpec<String> selectedFiles; | ||
private final OptionSpec<String> selectedDirectories; | ||
private final OptionSpec<Void> scanModulePath; | ||
private final OptionSpec<String> selectedModules; | ||
private final OptionSpec<String> selectedPackages; | ||
private final OptionSpec<String> selectedClasses; | ||
private final OptionSpec<String> selectedMethods; | ||
|
@@ -150,6 +152,16 @@ class AvailableOptions { | |
"Select a classpath resource for test discovery. This option can be repeated.") // | ||
.withRequiredArg(); | ||
|
||
// --- Java Platform Module System ------------------------------------- | ||
|
||
scanModulePath = parser.acceptsAll(asList("scan-modulepath", "scan-module-path"), // | ||
"EXPERIMENTAL: Scan all modules on the module-path for test discovery."); | ||
|
||
selectedModules = parser.acceptsAll(asList("o", "select-module"), // | ||
"EXPERIMENTAL: Select single module for test discovery. This option can be repeated.") // | ||
.withRequiredArg() // | ||
.describedAs("module name"); | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we also add support in the Vintage engine? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Mh? Vintage should support both new selectors. If it doesn't then something was lost in rebasing to master. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Find vintage-affecting changes below, around https://github.com/junit-team/junit5/pull/1061/files#diff-5e437a25c16596592cf66086916f6fed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, missed that! LGTM! |
||
// --- Filters --------------------------------------------------------- | ||
|
||
includeClassNamePattern = parser.acceptsAll(asList("n", "include-classname"), | ||
|
@@ -217,6 +229,8 @@ CommandLineOptions toCommandLineOptions(OptionSet detectedOptions) { | |
result.setSelectedUris(detectedOptions.valuesOf(this.selectedUris)); | ||
result.setSelectedFiles(detectedOptions.valuesOf(this.selectedFiles)); | ||
result.setSelectedDirectories(detectedOptions.valuesOf(this.selectedDirectories)); | ||
result.setScanModulepath(detectedOptions.has(this.scanModulePath)); | ||
result.setSelectedModules(detectedOptions.valuesOf(this.selectedModules)); | ||
result.setSelectedPackages(detectedOptions.valuesOf(this.selectedPackages)); | ||
result.setSelectedClasses(detectedOptions.valuesOf(this.selectedClasses)); | ||
result.setSelectedMethods(detectedOptions.valuesOf(this.selectedMethods)); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could limit the single service implementation to be our own ... by comparing the class name or some other "magic ID". Or is it okay to allow third-party implementations here? Thoughts?