package spoon.reflect.visitor;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.mockito.Mockito;
import spoon.reflect.factory.CoreFactory;
import spoon.reflect.factory.Factory;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static spoon.testing.utils.ModelUtils.createFactory;
/**
*
* Tests the main contract of CtInheritanceScanner
*
* Can be called with
* $ mvn test -D test=spoon.reflect.visitor.CtInheritanceScannerTest
*
* Created by nicolas on 25/02/2015.
*/
@RunWith(Parameterized.class)
public class CtInheritanceScannerTest<T extends CtVisitable> {
private static Factory factory = createFactory();
@Parameterized.Parameters(name = "{0}")
public static Collection<Object[]> data() throws Exception {
List<Object[]> values = new ArrayList<>();
for (Method method : CoreFactory.class.getDeclaredMethods()) {
if (method.getName().startsWith("create")
&& method.getParameterCount() == 0
&& method.getReturnType().getSimpleName().startsWith("Ct")) {
values.add(new Object[] { method.getReturnType(), method.invoke(factory.Core()) });
}
}
return values;
}
@Parameterized.Parameter(0)
public Class<T> toTest;
@Parameterized.Parameter(1)
public T instance;
/**
* Create the list of method we have to call for a class
*
* @param entry
* @return
* @throws Exception
*/
private List<Method> getMethodToInvoke(Class<?> entry) throws Exception {
Queue<Class<?>> tocheck = new LinkedList<>();
tocheck.add(entry);
List<Method> toInvoke = new ArrayList<>();
while (!tocheck.isEmpty()) {
Class<?> intf = tocheck.poll();
Assert.assertTrue(intf.isInterface());
if (!intf.getSimpleName().startsWith("Ct")) {
continue;
}
Method mth=null;
// if a method visitX exists, it must be invoked
try {
mth = CtInheritanceScanner.class.getDeclaredMethod("visit" + intf.getSimpleName(), intf);
if (mth.getAnnotation(Deprecated.class) != null) {
// if the method visitX exists with a deprecated annotation, it mustn't be invoked.
mth = null;
}
} catch (NoSuchMethodException ex) {
// no such method, nothing
}
if (mth!=null && !toInvoke.contains(mth)) {
toInvoke.add(mth);
}
// if a method scanX exists, it must be invoked
try {
mth = CtInheritanceScanner.class.getDeclaredMethod("scan" + intf.getSimpleName(), intf);
if (mth.getAnnotation(Deprecated.class) != null) {
// if the method scanX exists with a deprecated annotation, it mustn't be invoked.
mth = null;
}
} catch (NoSuchMethodException ex) {
// no such method, nothing
}
if (mth!=null && !toInvoke.contains(mth)) {
toInvoke.add(mth);
}
// recursion
for (Class<?> aClass : intf.getInterfaces()) {
tocheck.add(aClass);
}
}
return toInvoke;
}
/**
* A return element is a flow break and a statement
*/
@Test
public void testCtInheritanceScanner() throws Throwable {
CtInheritanceScanner mocked = mock(CtInheritanceScanner.class);
List<Method> toInvoke = getMethodToInvoke(toTest);
// we invoke super for all method we attempt to call
for (Method method : toInvoke) {
method.invoke(Mockito.doCallRealMethod().when(mocked), instance);
}
instance.accept(mocked);
// verify we call all methods
for (int i = 0; i < toInvoke.size(); i++) {
try {
toInvoke.get(i).invoke(verify(mocked), instance);
} catch (InvocationTargetException e) {
if (e.getTargetException() instanceof AssertionError) {
fail("visit"+instance.getClass().getSimpleName().replaceAll("Impl$", "")+" does not call "+toInvoke.get(i).getName());
} else {
throw e.getTargetException();
}
}
}
}
}