TestDelegatespublic class TestDelegates extends TestCase Tests that native delegate classes implement all the required methods.
This looks at {@link CreateInfo#DELEGATE_CLASS_NATIVES} to get the list of classes that
have their native methods reimplemented through a delegate.
Since the reimplemented methods are not native anymore, we look for the annotation
{@link LayoutlibDelegate}, and look for a matching method in the delegate (named the same
as the modified class with _Delegate added as a suffix).
If the original native method is not static, then we make sure the delegate method also
include the original class as first parameter (to access "this"). |
Fields Summary |
---|
private List | mErrors |
Methods Summary |
---|
private void | compare(java.lang.Class originalClass, java.lang.Class delegateClass)
List<Method> checkedDelegateMethods = new ArrayList<Method>();
// loop on the methods of the original class, and for the ones that are annotated
// with @LayoutlibDelegate, look for a matching method in the delegate class.
// The annotation is automatically added by layoutlib_create when it replace a method
// by a call to a delegate
Method[] originalMethods = originalClass.getDeclaredMethods();
for (Method originalMethod : originalMethods) {
// look for methods that are delegated: they have the LayoutlibDelegate annotation
if (originalMethod.getAnnotation(LayoutlibDelegate.class) == null) {
continue;
}
// get the signature.
Class<?>[] parameters = originalMethod.getParameterTypes();
// if the method is not static, then the class is added as the first parameter
// (for "this")
if ((originalMethod.getModifiers() & Modifier.STATIC) == 0) {
Class<?>[] newParameters = new Class<?>[parameters.length + 1];
newParameters[0] = originalClass;
System.arraycopy(parameters, 0, newParameters, 1, parameters.length);
parameters = newParameters;
}
// if the original class is an inner class that's not static, then
// we add this on the enclosing class at the beginning
if (originalClass.getEnclosingClass() != null &&
(originalClass.getModifiers() & Modifier.STATIC) == 0) {
Class<?>[] newParameters = new Class<?>[parameters.length + 1];
newParameters[0] = originalClass.getEnclosingClass();
System.arraycopy(parameters, 0, newParameters, 1, parameters.length);
parameters = newParameters;
}
try {
// try to load the method with the given parameter types.
Method delegateMethod = delegateClass.getDeclaredMethod(originalMethod.getName(),
parameters);
// check the return type of the methods match.
if (delegateMethod.getReturnType() != originalMethod.getReturnType()) {
mErrors.add(
String.format("Delegate method %1$s.%2$s does not match the " +
"corresponding framework method which returns %3$s",
delegateClass.getName(),
getMethodName(delegateMethod),
originalMethod.getReturnType().getName()));
}
// check that the method has the annotation
if (delegateMethod.getAnnotation(LayoutlibDelegate.class) == null) {
mErrors.add(
String.format("Delegate method %1$s for class %2$s does not have the " +
"@LayoutlibDelegate annotation",
delegateMethod.getName(),
originalClass.getName()));
}
// check that the method is static
if ((delegateMethod.getModifiers() & Modifier.STATIC) != Modifier.STATIC) {
mErrors.add(
String.format(
"Delegate method %1$s for class %2$s is not static",
delegateMethod.getName(),
originalClass.getName())
);
}
// add the method as checked.
checkedDelegateMethods.add(delegateMethod);
} catch (NoSuchMethodException e) {
String name = getMethodName(originalMethod, parameters);
mErrors.add(String.format("Missing %1$s.%2$s", delegateClass.getName(), name));
}
}
// look for dead (delegate) code.
// This looks for all methods in the delegate class, and if they have the
// @LayoutlibDelegate annotation, make sure they have been previously found as a
// match for a method in the original class.
// If not, this means the method is a delegate for a method that either doesn't exist
// anymore or is not delegated anymore.
Method[] delegateMethods = delegateClass.getDeclaredMethods();
for (Method delegateMethod : delegateMethods) {
// look for methods that are delegates: they have the LayoutlibDelegate annotation
if (delegateMethod.getAnnotation(LayoutlibDelegate.class) == null) {
continue;
}
if (!checkedDelegateMethods.contains(delegateMethod)) {
mErrors.add(String.format(
"Delegate method %1$s.%2$s is not used anymore and must be removed",
delegateClass.getName(),
getMethodName(delegateMethod)));
}
}
| private java.lang.String | getErrors()
StringBuilder s = new StringBuilder();
for (String error : mErrors) {
s.append(error).append('\n");
}
return s.toString();
| private java.lang.String | getMethodName(java.lang.reflect.Method method)
return getMethodName(method, method.getParameterTypes());
| private java.lang.String | getMethodName(java.lang.reflect.Method method, java.lang.Class[] parameters)
// compute a full class name that's long but not too long.
StringBuilder sb = new StringBuilder(method.getName() + "(");
for (int j = 0; j < parameters.length; j++) {
Class<?> theClass = parameters[j];
sb.append(theClass.getName());
int dimensions = 0;
while (theClass.isArray()) {
dimensions++;
theClass = theClass.getComponentType();
}
for (int i = 0; i < dimensions; i++) {
sb.append("[]");
}
if (j < (parameters.length - 1)) {
sb.append(",");
}
}
sb.append(")");
return sb.toString();
| private void | loadAndCompareClasses(java.lang.String originalClassName, java.lang.String delegateClassName)
// load the classes
try {
ClassLoader classLoader = TestDelegates.class.getClassLoader();
Class<?> originalClass = classLoader.loadClass(originalClassName);
Class<?> delegateClass = classLoader.loadClass(delegateClassName);
compare(originalClass, delegateClass);
} catch (ClassNotFoundException e) {
mErrors.add("Failed to load class: " + e.getMessage());
} catch (SecurityException e) {
mErrors.add("Failed to load class: " + e.getMessage());
}
| public void | testMethodDelegates()
final String[] methods = CreateInfo.DELEGATE_METHODS;
mErrors.clear();
for (String methodName : methods) {
// extract the class name
String className = methodName.substring(0, methodName.indexOf('#"));
String targetClassName = className.replace('$", '_") + "_Delegate";
loadAndCompareClasses(className, targetClassName);
}
assertTrue(getErrors(), mErrors.isEmpty());
| public void | testNativeDelegates()
final String[] classes = CreateInfo.DELEGATE_CLASS_NATIVES;
mErrors.clear();
for (String clazz : classes) {
loadAndCompareClasses(clazz, clazz + "_Delegate");
}
assertTrue(getErrors(), mErrors.isEmpty());
|
|