package org.ovirt.engine.core.utils;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.lang.reflect.Constructor;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import org.apache.commons.lang.ArrayUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
/**
* Tests for the {@link ReflectionUtils} class.
*
*/
@RunWith(Parameterized.class)
public class ReflectionUtilsFindConstructorTest {
/* --- Class Fields --- */
/** The type to test the method with. */
private final Class<?> typeToTest;
/**
* The parameters used to look for an empty ctor (unless non-static inner class, should be an empty array).
*/
private final Class<?>[] parametersForEmptyCase;
/** The parameters used to look for the exact case. */
private final Class<?>[] parametersForExactCase;
/** The parameters used for the negative case of super type. */
private final Class<?>[] parametersForSupertypeCase;
/** The parameters used for the positive case of sub type. */
private final Class<?>[] parametersForSubtypeCase;
/* --- Private classes used for testing the findConstructor method. --- */
@SuppressWarnings("unused")
private static class ClassWithEmptyPublicCtor {
public ClassWithEmptyPublicCtor() {
}
public ClassWithEmptyPublicCtor(Exception unused) {
}
public class InnerClass {
public InnerClass() {
}
public InnerClass(Exception unused) {
}
}
public static class StaticInnerClass {
public StaticInnerClass() {
}
public StaticInnerClass(Exception unused) {
}
}
}
@SuppressWarnings("unused")
private static class ClassWithEmptyProtectedCtor {
protected ClassWithEmptyProtectedCtor() {
}
protected ClassWithEmptyProtectedCtor(Exception unused) {
}
}
private static class ClassWithEmptyPrivateCtor {
private ClassWithEmptyPrivateCtor() {
}
private ClassWithEmptyPrivateCtor(Exception unused) {
}
}
@SuppressWarnings("unused")
private static class GenericizedClass<T extends Exception> {
public GenericizedClass() {
}
public GenericizedClass(T param) {
}
}
/* --- Constructors --- */
/**
* Construct a test case for the given test data.
*
* @param typeToTest
* The type to test the method with.
* @param parametersForEmptyCase
* The parameters used to look for an empty ctor (unless non-static inner class, should be an empty
* array).
* @param parametersForExactCase
* The parameters used to look for the exact case.
* @param parametersForSupertypeCase
* The parameters used for the negative case of super type.
* @param parametersForSubtypeCase
* The parameters used for the positive case of sub type.
*/
public ReflectionUtilsFindConstructorTest(Class<?> typeToTest,
Class<?>[] parametersForEmptyCase,
Class<?>[] parametersForExactCase,
Class<?>[] parametersForSupertypeCase,
Class<?>[] parametersForSubtypeCase) {
this.typeToTest = typeToTest;
this.parametersForEmptyCase = parametersForEmptyCase;
this.parametersForExactCase = parametersForExactCase;
this.parametersForSupertypeCase = parametersForSupertypeCase;
this.parametersForSubtypeCase = parametersForSubtypeCase;
}
/* --- Parameters generation --- */
@Parameters
public static Collection<Object[]> parameters() {
Collection<Object[]> ret = new ArrayList<>(5);
ret.add(new Object[] { ClassWithEmptyPublicCtor.class, new Class<?>[0], new Class<?>[] { Exception.class },
new Class<?>[] { Throwable.class }, new Class<?>[] { RuntimeException.class } });
ret.add(new Object[] { ClassWithEmptyProtectedCtor.class, new Class<?>[0], new Class<?>[] { Exception.class },
new Class<?>[] { Throwable.class }, new Class<?>[] { RuntimeException.class } });
ret.add(new Object[] { ClassWithEmptyPrivateCtor.class, new Class<?>[0], new Class<?>[] { Exception.class },
new Class<?>[] { Throwable.class }, new Class<?>[] { RuntimeException.class } });
ret.add(new Object[] { GenericizedClass.class, new Class<?>[0], new Class<?>[] { Exception.class },
new Class<?>[] { Throwable.class }, new Class<?>[] { RuntimeException.class } });
ret.add(new Object[] { ClassWithEmptyPublicCtor.InnerClass.class,
new Class<?>[] { ClassWithEmptyPublicCtor.class },
new Class<?>[] { ClassWithEmptyPublicCtor.class, Exception.class },
new Class<?>[] { ClassWithEmptyPublicCtor.class, Throwable.class },
new Class<?>[] { ClassWithEmptyPublicCtor.class, RuntimeException.class } });
ret.add(new Object[] { ClassWithEmptyPublicCtor.StaticInnerClass.class, new Class<?>[0],
new Class<?>[] { Exception.class }, new Class<?>[] { Throwable.class },
new Class<?>[] { RuntimeException.class } });
return ret;
}
/* --- Tests for the positive cases --- */
/**
* Test that {@link ReflectionUtils#findConstructor(Class, Class...)} finds the constructor when no parameter types
* are passed.
*/
@Test
public void positiveTestFindConstructorForNoParams() throws Exception {
findConstructorAndAssertItWasFound(parametersForEmptyCase);
}
/**
* Test that {@link ReflectionUtils#findConstructor(Class, Class...)} finds the constructor when the exact parameter
* is passed.
*/
@Test
public void positiveTestFindConstructorForExactParam() throws Exception {
findConstructorAndAssertItWasFound(parametersForExactCase);
}
/**
* Test that {@link ReflectionUtils#findConstructor(Class, Class...)} finds the constructor when the subtype of the
* parameter is passed.
*/
@Test
public void positiveTestFindConstructorForSubtypeOfParam() throws Exception {
findConstructorAndAssertItWasFound(parametersForSubtypeCase);
}
/* --- Tests for the negative cases --- */
/**
* Test that {@link ReflectionUtils#findConstructor(Class, Class...)} doesn't find a constructor when the supertype
* is used for lookup.
*/
@Test
public void negativeTestFindConstructorForSupertype() throws Exception {
assertNull(createAssertMessage("ctor was found anyway"),
ReflectionUtils.findConstructor(typeToTest, parametersForSupertypeCase));
}
/**
* Test that {@link ReflectionUtils#findConstructor(Class, Class...)} doesn't find a constructor when the number of
* arguments doesn't match anything.
*/
@Test
public void negativeTestFindConstructorForMismatchingArgumentsNumber() throws Exception {
assertNull(createAssertMessage("ctor was found anyway"),
ReflectionUtils.findConstructor(typeToTest,
(Class<?>[]) ArrayUtils.add(parametersForExactCase, Object.class)));
}
/* --- Helper Methods --- */
/**
* Find the constructor with the given parameters and check that it was found and has the correct number and type of
* parameters.
*
* @param parameters
* The parameters to check with.
*/
private void findConstructorAndAssertItWasFound(Class<?>[] parameters) {
Constructor<?> constructor = ReflectionUtils.findConstructor(typeToTest, parameters);
assertNotNull(createAssertMessage("ctor not found"), constructor);
assertEquals(createAssertMessage("found ctor with wrong parameter length"), parameters.length,
constructor.getParameterTypes().length);
Class<?>[] ctorParameters = constructor.getParameterTypes();
for (int i = 0; i < parameters.length; i++) {
assertTrue(createAssertMessage("parameter not compatible, expected {1} or a subclass, but was {2}",
parameters[i].getSimpleName(),
ctorParameters[i].getSimpleName()),
ctorParameters[i].isAssignableFrom(parameters[i]));
}
}
/**
* Create an assertion message with the class type that was checked, because the parameterized tests don't have this
* info.
*
* @param message
* Additional message to print.
* @param arguments
* Additional arguments (if any).
*
* @return The assertion message.
*/
private String createAssertMessage(String message, Object... arguments) {
return MessageFormat.format("Test for type {0} failed: " + message,
ArrayUtils.addAll(new Object[] { typeToTest.getSimpleName() }, arguments));
}
}