aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org/junit/runners/model
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org/junit/runners/model')
-rw-r--r--src/main/java/org/junit/runners/model/FrameworkField.java21
-rw-r--r--src/main/java/org/junit/runners/model/FrameworkMember.java24
-rw-r--r--src/main/java/org/junit/runners/model/FrameworkMethod.java14
-rw-r--r--src/main/java/org/junit/runners/model/InitializationError.java2
-rw-r--r--src/main/java/org/junit/runners/model/InvalidTestClassError.java39
-rw-r--r--src/main/java/org/junit/runners/model/MemberValueConsumer.java18
-rw-r--r--src/main/java/org/junit/runners/model/MultipleFailureException.java41
-rw-r--r--src/main/java/org/junit/runners/model/RunnerBuilder.java30
-rw-r--r--[-rwxr-xr-x]src/main/java/org/junit/runners/model/TestClass.java57
9 files changed, 222 insertions, 24 deletions
diff --git a/src/main/java/org/junit/runners/model/FrameworkField.java b/src/main/java/org/junit/runners/model/FrameworkField.java
index 945e389..ea2b16f 100644
--- a/src/main/java/org/junit/runners/model/FrameworkField.java
+++ b/src/main/java/org/junit/runners/model/FrameworkField.java
@@ -14,12 +14,26 @@ import org.junit.runners.BlockJUnit4ClassRunner;
public class FrameworkField extends FrameworkMember<FrameworkField> {
private final Field field;
- FrameworkField(Field field) {
+ /**
+ * Returns a new {@code FrameworkField} for {@code field}.
+ *
+ * <p>Access relaxed to {@code public} since version 4.13.1.
+ */
+ public FrameworkField(Field field) {
if (field == null) {
throw new NullPointerException(
"FrameworkField cannot be created without an underlying field.");
}
this.field = field;
+
+ if (isPublic()) {
+ // This field could be a public field in a package-scope base class
+ try {
+ field.setAccessible(true);
+ } catch (SecurityException e) {
+ // We may get an IllegalAccessException when we try to access the field
+ }
+ }
}
@Override
@@ -41,6 +55,11 @@ public class FrameworkField extends FrameworkMember<FrameworkField> {
}
@Override
+ boolean isBridgeMethod() {
+ return false;
+ }
+
+ @Override
protected int getModifiers() {
return field.getModifiers();
}
diff --git a/src/main/java/org/junit/runners/model/FrameworkMember.java b/src/main/java/org/junit/runners/model/FrameworkMember.java
index 724f096..5634b3f 100644
--- a/src/main/java/org/junit/runners/model/FrameworkMember.java
+++ b/src/main/java/org/junit/runners/model/FrameworkMember.java
@@ -12,15 +12,29 @@ public abstract class FrameworkMember<T extends FrameworkMember<T>> implements
Annotatable {
abstract boolean isShadowedBy(T otherMember);
- boolean isShadowedBy(List<T> members) {
- for (T each : members) {
- if (isShadowedBy(each)) {
- return true;
+ T handlePossibleBridgeMethod(List<T> members) {
+ for (int i = members.size() - 1; i >=0; i--) {
+ T otherMember = members.get(i);
+ if (isShadowedBy(otherMember)) {
+ if (otherMember.isBridgeMethod()) {
+ /*
+ * We need to return the previously-encountered bridge method
+ * because JUnit won't be able to call the parent method,
+ * because the parent class isn't public.
+ */
+ members.remove(i);
+ return otherMember;
+ }
+ // We found a shadowed member that isn't a bridge method. Ignore it.
+ return null;
}
}
- return false;
+ // No shadow or bridge method found. The caller should add *this* member.
+ return (T) this;
}
+ abstract boolean isBridgeMethod();
+
protected abstract int getModifiers();
/**
diff --git a/src/main/java/org/junit/runners/model/FrameworkMethod.java b/src/main/java/org/junit/runners/model/FrameworkMethod.java
index 3580052..4471407 100644
--- a/src/main/java/org/junit/runners/model/FrameworkMethod.java
+++ b/src/main/java/org/junit/runners/model/FrameworkMethod.java
@@ -28,6 +28,15 @@ public class FrameworkMethod extends FrameworkMember<FrameworkMethod> {
"FrameworkMethod cannot be created without an underlying method.");
}
this.method = method;
+
+ if (isPublic()) {
+ // This method could be a public method in a package-scope base class
+ try {
+ method.setAccessible(true);
+ } catch (SecurityException e) {
+ // We may get an IllegalAccessException when we try to call the method
+ }
+ }
}
/**
@@ -149,6 +158,11 @@ public class FrameworkMethod extends FrameworkMember<FrameworkMethod> {
}
@Override
+ boolean isBridgeMethod() {
+ return method.isBridge();
+ }
+
+ @Override
public boolean equals(Object obj) {
if (!FrameworkMethod.class.isInstance(obj)) {
return false;
diff --git a/src/main/java/org/junit/runners/model/InitializationError.java b/src/main/java/org/junit/runners/model/InitializationError.java
index 841b565..dd9c8b3 100644
--- a/src/main/java/org/junit/runners/model/InitializationError.java
+++ b/src/main/java/org/junit/runners/model/InitializationError.java
@@ -14,7 +14,7 @@ public class InitializationError extends Exception {
/*
* We have to use the f prefix until the next major release to ensure
* serialization compatibility.
- * See https://github.com/junit-team/junit/issues/976
+ * See https://github.com/junit-team/junit4/issues/976
*/
private final List<Throwable> fErrors;
diff --git a/src/main/java/org/junit/runners/model/InvalidTestClassError.java b/src/main/java/org/junit/runners/model/InvalidTestClassError.java
new file mode 100644
index 0000000..57be610
--- /dev/null
+++ b/src/main/java/org/junit/runners/model/InvalidTestClassError.java
@@ -0,0 +1,39 @@
+package org.junit.runners.model;
+
+import java.util.List;
+
+/**
+ * Thrown by {@link org.junit.runner.Runner}s in case the class under test is not valid.
+ * <p>
+ * Its message conveniently lists all of the validation errors.
+ *
+ * @since 4.13
+ */
+public class InvalidTestClassError extends InitializationError {
+ private static final long serialVersionUID = 1L;
+
+ private final String message;
+
+ public InvalidTestClassError(Class<?> offendingTestClass, List<Throwable> validationErrors) {
+ super(validationErrors);
+ this.message = createMessage(offendingTestClass, validationErrors);
+ }
+
+ private static String createMessage(Class<?> testClass, List<Throwable> validationErrors) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(String.format("Invalid test class '%s':", testClass.getName()));
+ int i = 1;
+ for (Throwable error : validationErrors) {
+ sb.append("\n " + (i++) + ". " + error.getMessage());
+ }
+ return sb.toString();
+ }
+
+ /**
+ * @return a message with a list of all of the validation errors
+ */
+ @Override
+ public String getMessage() {
+ return message;
+ }
+}
diff --git a/src/main/java/org/junit/runners/model/MemberValueConsumer.java b/src/main/java/org/junit/runners/model/MemberValueConsumer.java
new file mode 100644
index 0000000..a6157bf
--- /dev/null
+++ b/src/main/java/org/junit/runners/model/MemberValueConsumer.java
@@ -0,0 +1,18 @@
+package org.junit.runners.model;
+
+/**
+ * Represents a receiver for values of annotated fields/methods together with the declaring member.
+ *
+ * @see TestClass#collectAnnotatedFieldValues(Object, Class, Class, MemberValueConsumer)
+ * @see TestClass#collectAnnotatedMethodValues(Object, Class, Class, MemberValueConsumer)
+ * @since 4.13
+ */
+public interface MemberValueConsumer<T> {
+ /**
+ * Receives the next value and its declaring member.
+ *
+ * @param member declaring member ({@link FrameworkMethod} or {@link FrameworkField})
+ * @param value the value of the next member
+ */
+ void accept(FrameworkMember<?> member, T value);
+}
diff --git a/src/main/java/org/junit/runners/model/MultipleFailureException.java b/src/main/java/org/junit/runners/model/MultipleFailureException.java
index 325c645..8e355a7 100644
--- a/src/main/java/org/junit/runners/model/MultipleFailureException.java
+++ b/src/main/java/org/junit/runners/model/MultipleFailureException.java
@@ -1,9 +1,13 @@
package org.junit.runners.model;
+import java.io.PrintStream;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import org.junit.TestCouldNotBeSkippedException;
+import org.junit.internal.AssumptionViolatedException;
import org.junit.internal.Throwables;
/**
@@ -17,12 +21,22 @@ public class MultipleFailureException extends Exception {
/*
* We have to use the f prefix until the next major release to ensure
* serialization compatibility.
- * See https://github.com/junit-team/junit/issues/976
+ * See https://github.com/junit-team/junit4/issues/976
*/
private final List<Throwable> fErrors;
public MultipleFailureException(List<Throwable> errors) {
- this.fErrors = new ArrayList<Throwable>(errors);
+ if (errors.isEmpty()) {
+ throw new IllegalArgumentException(
+ "List of Throwables must not be empty");
+ }
+ this.fErrors = new ArrayList<Throwable>(errors.size());
+ for (Throwable error : errors) {
+ if (error instanceof AssumptionViolatedException) {
+ error = new TestCouldNotBeSkippedException((AssumptionViolatedException) error);
+ }
+ fErrors.add(error);
+ }
}
public List<Throwable> getFailures() {
@@ -34,11 +48,32 @@ public class MultipleFailureException extends Exception {
StringBuilder sb = new StringBuilder(
String.format("There were %d errors:", fErrors.size()));
for (Throwable e : fErrors) {
- sb.append(String.format("\n %s(%s)", e.getClass().getName(), e.getMessage()));
+ sb.append(String.format("%n %s(%s)", e.getClass().getName(), e.getMessage()));
}
return sb.toString();
}
+ @Override
+ public void printStackTrace() {
+ for (Throwable e: fErrors) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void printStackTrace(PrintStream s) {
+ for (Throwable e: fErrors) {
+ e.printStackTrace(s);
+ }
+ }
+
+ @Override
+ public void printStackTrace(PrintWriter s) {
+ for (Throwable e: fErrors) {
+ e.printStackTrace(s);
+ }
+ }
+
/**
* Asserts that a list of throwables is empty. If it isn't empty,
* will throw {@link MultipleFailureException} (if there are
diff --git a/src/main/java/org/junit/runners/model/RunnerBuilder.java b/src/main/java/org/junit/runners/model/RunnerBuilder.java
index 7d3eee3..ba7c9e2 100644
--- a/src/main/java/org/junit/runners/model/RunnerBuilder.java
+++ b/src/main/java/org/junit/runners/model/RunnerBuilder.java
@@ -6,7 +6,11 @@ import java.util.List;
import java.util.Set;
import org.junit.internal.runners.ErrorReportingRunner;
+import org.junit.runner.Description;
+import org.junit.runner.OrderWith;
import org.junit.runner.Runner;
+import org.junit.runner.manipulation.InvalidOrderingException;
+import org.junit.runner.manipulation.Ordering;
/**
* A RunnerBuilder is a strategy for constructing runners for classes.
@@ -49,19 +53,39 @@ public abstract class RunnerBuilder {
public abstract Runner runnerForClass(Class<?> testClass) throws Throwable;
/**
- * Always returns a runner, even if it is just one that prints an error instead of running tests.
+ * Always returns a runner for the given test class.
+ *
+ * <p>In case of an exception a runner will be returned that prints an error instead of running
+ * tests.
+ *
+ * <p>Note that some of the internal JUnit implementations of RunnerBuilder will return
+ * {@code null} from this method, but no RunnerBuilder passed to a Runner constructor will
+ * return {@code null} from this method.
*
* @param testClass class to be run
* @return a Runner
*/
public Runner safeRunnerForClass(Class<?> testClass) {
try {
- return runnerForClass(testClass);
+ Runner runner = runnerForClass(testClass);
+ if (runner != null) {
+ configureRunner(runner);
+ }
+ return runner;
} catch (Throwable e) {
return new ErrorReportingRunner(testClass, e);
}
}
+ private void configureRunner(Runner runner) throws InvalidOrderingException {
+ Description description = runner.getDescription();
+ OrderWith orderWith = description.getAnnotation(OrderWith.class);
+ if (orderWith != null) {
+ Ordering ordering = Ordering.definedBy(orderWith.value(), description);
+ ordering.apply(runner);
+ }
+ }
+
Class<?> addParent(Class<?> parent) throws InitializationError {
if (!parents.add(parent)) {
throw new InitializationError(String.format("class '%s' (possibly indirectly) contains itself as a SuiteClass", parent.getName()));
@@ -96,7 +120,7 @@ public abstract class RunnerBuilder {
}
private List<Runner> runners(Class<?>[] children) {
- ArrayList<Runner> runners = new ArrayList<Runner>();
+ List<Runner> runners = new ArrayList<Runner>();
for (Class<?> each : children) {
Runner childRunner = safeRunnerForClass(each);
if (childRunner != null) {
diff --git a/src/main/java/org/junit/runners/model/TestClass.java b/src/main/java/org/junit/runners/model/TestClass.java
index c8a544d..5962c2b 100755..100644
--- a/src/main/java/org/junit/runners/model/TestClass.java
+++ b/src/main/java/org/junit/runners/model/TestClass.java
@@ -84,20 +84,21 @@ public class TestClass implements Annotatable {
for (Annotation each : member.getAnnotations()) {
Class<? extends Annotation> type = each.annotationType();
List<T> members = getAnnotatedMembers(map, type, true);
- if (member.isShadowedBy(members)) {
+ T memberToAdd = member.handlePossibleBridgeMethod(members);
+ if (memberToAdd == null) {
return;
}
if (runsTopToBottom(type)) {
- members.add(0, member);
+ members.add(0, memberToAdd);
} else {
- members.add(member);
+ members.add(memberToAdd);
}
}
}
private static <T extends FrameworkMember<T>> Map<Class<? extends Annotation>, List<T>>
makeDeeplyUnmodifiable(Map<Class<? extends Annotation>, List<T>> source) {
- LinkedHashMap<Class<? extends Annotation>, List<T>> copy =
+ Map<Class<? extends Annotation>, List<T>> copy =
new LinkedHashMap<Class<? extends Annotation>, List<T>>();
for (Map.Entry<Class<? extends Annotation>, List<T>> entry : source.entrySet()) {
copy.put(entry.getKey(), Collections.unmodifiableList(entry.getValue()));
@@ -168,7 +169,7 @@ public class TestClass implements Annotatable {
}
private static List<Class<?>> getSuperClasses(Class<?> testClass) {
- ArrayList<Class<?>> results = new ArrayList<Class<?>>();
+ List<Class<?>> results = new ArrayList<Class<?>>();
Class<?> current = testClass;
while (current != null) {
results.add(current);
@@ -224,24 +225,59 @@ public class TestClass implements Annotatable {
public <T> List<T> getAnnotatedFieldValues(Object test,
Class<? extends Annotation> annotationClass, Class<T> valueClass) {
- List<T> results = new ArrayList<T>();
+ final List<T> results = new ArrayList<T>();
+ collectAnnotatedFieldValues(test, annotationClass, valueClass,
+ new MemberValueConsumer<T>() {
+ public void accept(FrameworkMember<?> member, T value) {
+ results.add(value);
+ }
+ });
+ return results;
+ }
+
+ /**
+ * Finds the fields annotated with the specified annotation and having the specified type,
+ * retrieves the values and passes those to the specified consumer.
+ *
+ * @since 4.13
+ */
+ public <T> void collectAnnotatedFieldValues(Object test,
+ Class<? extends Annotation> annotationClass, Class<T> valueClass,
+ MemberValueConsumer<T> consumer) {
for (FrameworkField each : getAnnotatedFields(annotationClass)) {
try {
Object fieldValue = each.get(test);
if (valueClass.isInstance(fieldValue)) {
- results.add(valueClass.cast(fieldValue));
+ consumer.accept(each, valueClass.cast(fieldValue));
}
} catch (IllegalAccessException e) {
throw new RuntimeException(
"How did getFields return a field we couldn't access?", e);
}
}
- return results;
}
public <T> List<T> getAnnotatedMethodValues(Object test,
Class<? extends Annotation> annotationClass, Class<T> valueClass) {
- List<T> results = new ArrayList<T>();
+ final List<T> results = new ArrayList<T>();
+ collectAnnotatedMethodValues(test, annotationClass, valueClass,
+ new MemberValueConsumer<T>() {
+ public void accept(FrameworkMember<?> member, T value) {
+ results.add(value);
+ }
+ });
+ return results;
+ }
+
+ /**
+ * Finds the methods annotated with the specified annotation and returning the specified type,
+ * invokes it and pass the return value to the specified consumer.
+ *
+ * @since 4.13
+ */
+ public <T> void collectAnnotatedMethodValues(Object test,
+ Class<? extends Annotation> annotationClass, Class<T> valueClass,
+ MemberValueConsumer<T> consumer) {
for (FrameworkMethod each : getAnnotatedMethods(annotationClass)) {
try {
/*
@@ -254,14 +290,13 @@ public class TestClass implements Annotatable {
*/
if (valueClass.isAssignableFrom(each.getReturnType())) {
Object fieldValue = each.invokeExplosively(test);
- results.add(valueClass.cast(fieldValue));
+ consumer.accept(each, valueClass.cast(fieldValue));
}
} catch (Throwable e) {
throw new RuntimeException(
"Exception in " + each.getName(), e);
}
}
- return results;
}
public boolean isPublic() {