blob: f15fb288e4a6c202aff6a85144000fad5e4db377 [file] [log] [blame]
package org.junit.experimental.theories.internal;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.junit.Assume;
import org.junit.experimental.theories.DataPoint;
import org.junit.experimental.theories.DataPoints;
import org.junit.experimental.theories.ParameterSignature;
import org.junit.experimental.theories.ParameterSupplier;
import org.junit.experimental.theories.PotentialAssignment;
import org.junit.runners.model.FrameworkField;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.TestClass;
/**
* Supplies Theory parameters based on all public members of the target class.
*/
public class AllMembersSupplier extends ParameterSupplier {
static class MethodParameterValue extends PotentialAssignment {
private final FrameworkMethod method;
private MethodParameterValue(FrameworkMethod dataPointMethod) {
method = dataPointMethod;
}
@Override
public Object getValue() throws CouldNotGenerateValueException {
try {
return method.invokeExplosively(null);
} catch (IllegalArgumentException e) {
throw new RuntimeException(
"unexpected: argument length is checked");
} catch (IllegalAccessException e) {
throw new RuntimeException(
"unexpected: getMethods returned an inaccessible method");
} catch (Throwable throwable) {
DataPoint annotation = method.getAnnotation(DataPoint.class);
Assume.assumeTrue(annotation == null || !isAssignableToAnyOf(annotation.ignoredExceptions(), throwable));
throw new CouldNotGenerateValueException(throwable);
}
}
@Override
public String getDescription() throws CouldNotGenerateValueException {
return method.getName();
}
}
private final TestClass clazz;
/**
* Constructs a new supplier for {@code type}
*/
public AllMembersSupplier(TestClass type) {
clazz = type;
}
@Override
public List<PotentialAssignment> getValueSources(ParameterSignature sig) throws Throwable {
List<PotentialAssignment> list = new ArrayList<PotentialAssignment>();
addSinglePointFields(sig, list);
addMultiPointFields(sig, list);
addSinglePointMethods(sig, list);
addMultiPointMethods(sig, list);
return list;
}
private void addMultiPointMethods(ParameterSignature sig, List<PotentialAssignment> list) throws Throwable {
for (FrameworkMethod dataPointsMethod : getDataPointsMethods(sig)) {
Class<?> returnType = dataPointsMethod.getReturnType();
if ((returnType.isArray() && sig.canPotentiallyAcceptType(returnType.getComponentType())) ||
Iterable.class.isAssignableFrom(returnType)) {
try {
addDataPointsValues(returnType, sig, dataPointsMethod.getName(), list,
dataPointsMethod.invokeExplosively(null));
} catch (Throwable throwable) {
DataPoints annotation = dataPointsMethod.getAnnotation(DataPoints.class);
if (annotation != null && isAssignableToAnyOf(annotation.ignoredExceptions(), throwable)) {
return;
} else {
throw throwable;
}
}
}
}
}
private void addSinglePointMethods(ParameterSignature sig, List<PotentialAssignment> list) {
for (FrameworkMethod dataPointMethod : getSingleDataPointMethods(sig)) {
if (sig.canAcceptType(dataPointMethod.getType())) {
list.add(new MethodParameterValue(dataPointMethod));
}
}
}
private void addMultiPointFields(ParameterSignature sig, List<PotentialAssignment> list) {
for (final Field field : getDataPointsFields(sig)) {
Class<?> type = field.getType();
addDataPointsValues(type, sig, field.getName(), list, getStaticFieldValue(field));
}
}
private void addSinglePointFields(ParameterSignature sig, List<PotentialAssignment> list) {
for (final Field field : getSingleDataPointFields(sig)) {
Object value = getStaticFieldValue(field);
if (sig.canAcceptValue(value)) {
list.add(PotentialAssignment.forValue(field.getName(), value));
}
}
}
private void addDataPointsValues(Class<?> type, ParameterSignature sig, String name,
List<PotentialAssignment> list, Object value) {
if (type.isArray()) {
addArrayValues(sig, name, list, value);
}
else if (Iterable.class.isAssignableFrom(type)) {
addIterableValues(sig, name, list, (Iterable<?>) value);
}
}
private void addArrayValues(ParameterSignature sig, String name, List<PotentialAssignment> list, Object array) {
for (int i = 0; i < Array.getLength(array); i++) {
Object value = Array.get(array, i);
if (sig.canAcceptValue(value)) {
list.add(PotentialAssignment.forValue(name + "[" + i + "]", value));
}
}
}
private void addIterableValues(ParameterSignature sig, String name, List<PotentialAssignment> list, Iterable<?> iterable) {
Iterator<?> iterator = iterable.iterator();
int i = 0;
while (iterator.hasNext()) {
Object value = iterator.next();
if (sig.canAcceptValue(value)) {
list.add(PotentialAssignment.forValue(name + "[" + i + "]", value));
}
i += 1;
}
}
private Object getStaticFieldValue(final Field field) {
try {
return field.get(null);
} catch (IllegalArgumentException e) {
throw new RuntimeException(
"unexpected: field from getClass doesn't exist on object");
} catch (IllegalAccessException e) {
throw new RuntimeException(
"unexpected: getFields returned an inaccessible field");
}
}
private static boolean isAssignableToAnyOf(Class<?>[] typeArray, Object target) {
for (Class<?> type : typeArray) {
if (type.isAssignableFrom(target.getClass())) {
return true;
}
}
return false;
}
protected Collection<FrameworkMethod> getDataPointsMethods(ParameterSignature sig) {
return clazz.getAnnotatedMethods(DataPoints.class);
}
protected Collection<Field> getSingleDataPointFields(ParameterSignature sig) {
List<FrameworkField> fields = clazz.getAnnotatedFields(DataPoint.class);
Collection<Field> validFields = new ArrayList<Field>();
for (FrameworkField frameworkField : fields) {
validFields.add(frameworkField.getField());
}
return validFields;
}
protected Collection<Field> getDataPointsFields(ParameterSignature sig) {
List<FrameworkField> fields = clazz.getAnnotatedFields(DataPoints.class);
Collection<Field> validFields = new ArrayList<Field>();
for (FrameworkField frameworkField : fields) {
validFields.add(frameworkField.getField());
}
return validFields;
}
protected Collection<FrameworkMethod> getSingleDataPointMethods(ParameterSignature sig) {
return clazz.getAnnotatedMethods(DataPoint.class);
}
}