org.pantsbuild.tools.junit.impl.Util Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of junit-runner Show documentation
Show all versions of junit-runner Show documentation
A command line tool for running junit tests that provides functionality above and beyond
that provided by org.junit.runner.JUnitCore.
// Copyright 2015 Pants project contributors (see CONTRIBUTORS.md).
// Licensed under the Apache License, Version 2.0 (see LICENSE).
package org.pantsbuild.tools.junit.impl;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import org.junit.Ignore;
import org.junit.runner.Description;
import org.junit.runner.RunWith;
import org.junit.runner.notification.Failure;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
/**
* Utilities for working with junit test runs.
*/
final class Util {
static final Predicate IS_ANNOTATED_TEST_METHOD =
new Predicate() {
@Override public boolean apply(Method method) {
return Modifier.isPublic(method.getModifiers())
&& method.isAnnotationPresent(org.junit.Test.class);
}
};
static final Predicate> IS_PUBLIC_CONSTRUCTOR =
new Predicate>() {
@Override public boolean apply(Constructor> constructor) {
return Modifier.isPublic(constructor.getModifiers());
}
};
private Util() {
// utility
}
/**
* Returns {@code true} if the given {@code test} is {@literal @Ignore}d.
*
* @param test The test description to evaluate.
* @return {@code true} if the described test is marked as ignored.
*/
static boolean isIgnored(Description test) {
return test.getAnnotation(Ignore.class) != null;
}
/**
* Returns {@code true} if the given class is {@literal @Ignore}d
*
* @param clazz class instance to evaluate.
* @return {@code true} if the class is marked as ignored.
*/
static boolean isIgnored(Class> clazz) {
return clazz.isAnnotationPresent(Ignore.class);
}
/**
* Returns {@code true} if the given {@code test} is eligible for running. Runnable tests are
* those that are not {@literal @Ignore}d and have direct executable content (i.e.: not a test
* suite or other executable test aggregator).
*
* @param test The test description to evaluate.
* @return {@code true} if the described test will be run by a standard junit runner.
*/
static boolean isRunnable(Description test) {
return test.isTest() && !isIgnored(test);
}
/**
* Returns {@code false} if the given class is {@literal @Ignore}d
*
* @param clazz class instance to evaluate.
* @return {@code true} if the class is marked as ignored.
*/
static boolean isRunnable(Class> clazz) {
return isTestClass(clazz) && !isIgnored(clazz);
}
/**
* Returns {@code true} if the test failure represents an assertion failure.
*
* @param failure The failure to test.
* @return {@code true} if the failure was from an incorrect assertion, {@code false} otherwise.
*/
static boolean isAssertionFailure(Failure failure) {
return failure.getException() instanceof AssertionError;
}
/**
* Returns a pants-friendly formatted description of the test-case.
*
* Pants likes test-cases formatted as org.foo.bar.TestClassName#testMethodName
*
* @param description The test description to produce a formatted name for.
* @return The formatted name of the test-case, if possible. If the Description does not have a
* class name or a method name, falls back on the description.getDisplayName().
*/
static String getPantsFriendlyDisplayName(Description description) {
String className = description.getClassName();
String methodName = description.getMethodName();
String vanillaDisplayName = description.getDisplayName();
if (className.equals(vanillaDisplayName) || methodName.equals(vanillaDisplayName)) {
// This happens if the Description isn't actually describing a test method. We don't handle
// this, so just use the default formatting.
return vanillaDisplayName;
}
StringBuffer sb = new StringBuffer(className.length() + methodName.length() + 1);
sb.append(className);
sb.append("#");
sb.append(methodName);
return sb.toString();
}
/**
* Returns a sanitized suite name suitable for inclusion in a XML report.
*
* This is also used to generate an XML report's filename, so it is important that it does not
* contain special characters that are illegal on the filesystem.
*
* This strips out punction and whitespace, but leaves the '_', '.', and '-' symbols, and trailing
* periods are trimmed.
*
* @param name The name to sanitize. In most cases this is the class name of the test being run,
* but some frameworks (I'm looking at you, Cucumber) like to pass weird things like
* human-readable free-form textual descriptions of the tests, so we can't make assumptions.
* @return
*/
static String sanitizeSuiteName(String name) {
return name.replaceAll("[[\\p{Punct}][\\p{Space}]&&[^_.-]]", "-").replaceAll("[.]+$", "");
}
/**
* Support junit 3.x Test hierarchy.
*/
public static boolean isJunit3Test(Class> clazz) {
return junit.framework.Test.class.isAssignableFrom(clazz);
}
/**
* Support classes using junit 4.x custom runners.
*/
public static boolean isUsingCustomRunner(Class> clazz) {
return clazz.isAnnotationPresent(RunWith.class);
}
public static boolean isTestClass(final Class> clazz) {
// Must be a public concrete class to be a runnable junit Test.
if (clazz.isInterface()
|| Modifier.isAbstract(clazz.getModifiers())
|| !Modifier.isPublic(clazz.getModifiers())) {
return false;
}
// The class must have some public constructor to be instantiated by the runner being used
if (!Iterables.any(Arrays.asList(clazz.getConstructors()), IS_PUBLIC_CONSTRUCTOR)) {
return false;
}
if (isJunit3Test(clazz)) {
return true;
}
// Support classes using junit 4.x custom runners.
if (isUsingCustomRunner(clazz)) {
return true;
}
if (ScalaTestUtil.isScalaTestTest(clazz)) {
return true;
}
// Support junit 4.x @Test annotated methods.
return Iterables.any(Arrays.asList(clazz.getMethods()), IS_ANNOTATED_TEST_METHOD);
}
}