com.googlecode.junittoolbox.WildcardPatternSuite Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of junit-toolbox Show documentation
Show all versions of junit-toolbox Show documentation
Useful classes for writing automated tests with JUnit
The newest version!
package com.googlecode.junittoolbox;
import com.googlecode.junittoolbox.util.JUnit4TestChecker;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.AbstractFileFilter;
import org.apache.commons.io.filefilter.IOFileFilter;
import org.apache.commons.io.filefilter.TrueFileFilter;
import org.junit.experimental.categories.Categories;
import org.junit.runner.manipulation.Filter;
import org.junit.runner.manipulation.NoTestsRemainException;
import org.junit.runners.Suite;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.RunnerBuilder;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.*;
import java.util.regex.Pattern;
import static org.junit.experimental.categories.Categories.*;
/**
* A replacement for the JUnit runners {@link Suite} and {@link Categories},
* which allows you to specify the children classes of your test suite class
* using a wildcard pattern.
* Example:
* @RunWith(WildcardPatternSuite.class)
* @SuiteClasses("**/*Test.class")
* public class AllTests {}
*
* You can also specify multiple patterns as well as exclude patterns:
* @RunWith(WildcardPatternSuite.class)
* @SuiteClasses({"**/*Test.class", "!gui/**"})
* public class AllButGuiTests {}
*
* Because it is also a replacement for the {@link Categories} runner,
* you can use the standard JUnit annotations {@link IncludeCategory @IncludeCategory}
* and/or {@link ExcludeCategory @ExcludeCategory}:
* @RunWith(WildcardPatternSuite.class)
* @SuiteClasses("**/*Test.class")
* @IncludeCategory(SlowTests.class)
* public class OnlySlowTests {}
*
* If you want to specify more than one category to include/exclude,
* you can also use {@link IncludeCategories @IncludeCategories} and/or
* {@link ExcludeCategories @ExcludeCategories} annotations provided
* by JUnit Toolbox:
* @RunWith(WildcardPatternSuite.class)
* @SuiteClasses("**/*Test.class")
* @ExcludeCategories({SlowTests.class, FlakyTests.class})
* public class NormalTests {}
*
*/
public class WildcardPatternSuite extends Suite {
private static Class>[] getSuiteClasses(Class> klass) throws InitializationError {
org.junit.runners.Suite.SuiteClasses annotation1 = klass.getAnnotation(org.junit.runners.Suite.SuiteClasses.class);
com.googlecode.junittoolbox.SuiteClasses annotation2 = klass.getAnnotation(com.googlecode.junittoolbox.SuiteClasses.class);
if (annotation1 == null && annotation2 == null) {
throw new InitializationError("class " + klass.getName() + " must have a SuiteClasses annotation");
}
Class>[] suiteClasses1 = (annotation1 == null ? null : annotation1.value());
Class>[] suiteClasses2 = (annotation2 == null ? null : findSuiteClasses(klass, annotation2.value()));
return union(suiteClasses1, suiteClasses2);
}
private static Class>[] findSuiteClasses(Class> klass, String... wildcardPatterns) throws InitializationError {
File baseDir = getBaseDir(klass);
Set classFiles = findFiles(baseDir, wildcardPatterns);
if (classFiles.isEmpty()) {
throw new InitializationError("Did not find any *.class file using the specified wildcard patterns " + Arrays.toString(wildcardPatterns) + " relative to directory " + baseDir);
}
File testClassesDir = getClassesDir(klass);
String testClassesPath;
try {
testClassesPath = testClassesDir.getCanonicalPath().replace('\\', '/');
} catch (IOException e) {
throw new RuntimeException(e.getMessage());
}
List> testClasses = new ArrayList<>();
ClassLoader classLoader = klass.getClassLoader();
JUnit4TestChecker junit4TestChecker = new JUnit4TestChecker(classLoader);
for (File file : classFiles) {
try {
String canonicalPath = file.getCanonicalPath().replace('\\', '/');
if (!canonicalPath.startsWith(testClassesPath)) {
// Ignore all *.class files not contained in testClassesDir ...
continue;
}
String path = canonicalPath.substring(testClassesPath.length() + 1);
String className = path.substring(0, path.length() - ".class".length()).replace('/', '.');
Class> clazz = classLoader.loadClass(className);
if (junit4TestChecker.accept(clazz)) {
testClasses.add(clazz);
}
} catch (Exception e) {
throw new InitializationError("Failed to load " + file + " -- " + e.getMessage());
}
}
if (testClasses.isEmpty()) {
throw new InitializationError("Did not find any test classes using the specified wildcard patterns " + Arrays.toString(wildcardPatterns) + " relative to directory " + baseDir);
}
return testClasses.toArray(new Class[testClasses.size()]);
}
private static Set findFiles(File baseDir, String... wildcardPatterns) throws InitializationError {
try {
Set included = new HashSet<>();
Set excluded = new HashSet<>();
for (String wildcardPattern: wildcardPatterns) {
if (wildcardPattern == null) {
throw new InitializationError("wildcard pattern for the SuiteClasses annotation must not be null");
} else if (wildcardPattern.startsWith("!")) {
excluded.addAll(findFiles(baseDir, wildcardPattern.substring(1)));
} else {
if (!wildcardPattern.endsWith(".class")) {
throw new InitializationError("wildcard pattern for the SuiteClasses annotation must end with \".class\"");
}
included.addAll(findFiles(baseDir, wildcardPattern));
}
}
included.removeAll(excluded);
return included;
} catch (IOException e) {
throw new InitializationError("Failed to scan " + baseDir + " using wildcard patterns " + Arrays.toString(wildcardPatterns) + " -- " + e);
}
}
private static Collection findFiles(File baseDir, String wildcardPattern) throws InitializationError, IOException {
if (wildcardPattern.startsWith("/")) {
throw new InitializationError("wildcard pattern for the SuiteClasses annotation must not start with a '/' character");
}
while (wildcardPattern.startsWith("../")) {
baseDir = baseDir.getParentFile();
wildcardPattern = wildcardPattern.substring(3);
}
Pattern regex = convertWildcardPatternToRegex("/" + wildcardPattern);
String basePath = baseDir.getCanonicalPath().replace('\\', '/');
IOFileFilter fileFilter = new AbstractFileFilter() {
@Override
public boolean accept(File file) {
try {
// Never accept directories, hidden files, and inner classes ...
if (file.isDirectory() || file.isHidden() || file.getName().contains("$")) {
return false;
}
String canonicalPath = file.getCanonicalPath().replace('\\', '/');
if (canonicalPath.startsWith(basePath)) {
String path = canonicalPath.substring(basePath.length());
if (regex.matcher(path).matches()) {
return true;
}
}
return false;
} catch (IOException e) {
throw new RuntimeException(e.getMessage());
}
}
};
return FileUtils.listFiles(baseDir, fileFilter, TrueFileFilter.INSTANCE);
}
private static File getBaseDir(Class> klass) throws InitializationError {
URL klassUrl = klass.getResource(klass.getSimpleName() + ".class");
try {
return new File(klassUrl.toURI()).getParentFile();
} catch (URISyntaxException e) {
throw new InitializationError("Failed to determine directory of " + klass.getSimpleName() + ".class file: " + e.getMessage());
}
}
private static File getClassesDir(Class> klass) throws InitializationError {
URL classesDirUrl = klass.getProtectionDomain().getCodeSource().getLocation();
try {
return new File(classesDirUrl.toURI());
} catch (URISyntaxException e) {
throw new InitializationError("Failed to determine classes directory of " + klass.getName() + " class: " + e.getMessage());
}
}
private static Pattern convertWildcardPatternToRegex(String wildCardPattern) throws InitializationError {
String s = wildCardPattern;
while (s.contains("***")) {
s = s.replace("***", "**");
}
String suffix;
if (s.endsWith("/**")) {
s = s.substring(0, s.length() - 3);
suffix = "(.*)";
} else {
suffix ="";
}
s = s.replace(".", "[.]");
s = s.replace("/**/", "/::/");
s = s.replace("*", "([^/]*)");
s = s.replace("/::/", "((/.*/)|(/))");
s = s.replace("?", ".");
if (s.contains("**")) {
throw new InitializationError("Invalid wildcard pattern \"" + wildCardPattern + "\"");
}
return Pattern.compile(s + suffix);
}
private static Class>[] union(Class>[] suiteClasses1, Class>[] suiteClasses2) {
if (suiteClasses1 == null) {
return suiteClasses2;
} else if (suiteClasses2 == null) {
return suiteClasses1;
} else {
HashSet> temp = new HashSet<>();
temp.addAll(Arrays.asList(suiteClasses1));
temp.addAll(Arrays.asList(suiteClasses2));
Class>[] result = new Class>[temp.size()];
temp.toArray(result);
return result;
}
}
public WildcardPatternSuite(Class> klass, RunnerBuilder builder) throws InitializationError {
super(builder, klass, getSuiteClasses(klass));
Filter filter = CategoriesFilter.forTestSuite(klass);
if (filter != null) {
try {
filter(filter);
} catch (NoTestsRemainException e) {
throw new InitializationError(e);
}
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy