Home | History | Annotate | Download | only in collect
      1 /*
      2  * Copyright (C) 2007 The Guava Authors
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.google.common.collect;
     18 
     19 import com.google.common.base.Function;
     20 import com.google.common.base.Joiner;
     21 
     22 import junit.framework.TestCase;
     23 
     24 import java.lang.reflect.Array;
     25 import java.lang.reflect.InvocationHandler;
     26 import java.lang.reflect.InvocationTargetException;
     27 import java.lang.reflect.Method;
     28 import java.lang.reflect.Proxy;
     29 import java.util.ArrayList;
     30 import java.util.Arrays;
     31 import java.util.Collection;
     32 import java.util.Collections;
     33 import java.util.Iterator;
     34 import java.util.List;
     35 import java.util.Set;
     36 
     37 /**
     38  * Base test case for testing the variety of forwarding classes.
     39  *
     40  * @author Robert Konigsberg
     41  * @author Louis Wasserman
     42  */
     43 public abstract class ForwardingTestCase extends TestCase {
     44 
     45   private List<String> calls = new ArrayList<String>();
     46 
     47   private void called(String id) {
     48     calls.add(id);
     49   }
     50 
     51   protected String getCalls() {
     52     return calls.toString();
     53   }
     54 
     55   protected boolean isCalled() {
     56     return !calls.isEmpty();
     57   }
     58 
     59   @SuppressWarnings("unchecked")
     60   protected <T> T createProxyInstance(Class<T> c) {
     61     /*
     62      * This invocation handler only registers that a method was called,
     63      * and then returns a bogus, but acceptable, value.
     64      */
     65     InvocationHandler handler = new InvocationHandler() {
     66       @Override
     67       public Object invoke(Object proxy, Method method, Object[] args)
     68           throws Throwable {
     69         called(asString(method));
     70 
     71         return getDefaultValue(method.getReturnType());
     72       }
     73     };
     74 
     75     return (T) Proxy.newProxyInstance(c.getClassLoader(),
     76         new Class[] { c }, handler);
     77   }
     78 
     79   private static final Joiner COMMA_JOINER = Joiner.on(",");
     80 
     81   /*
     82    * Returns string representation of a method.
     83    *
     84    * If the method takes no parameters, it returns the name (e.g.
     85    * "isEmpty". If the method takes parameters, it returns the simple names
     86    * of the parameters (e.g. "put(Object,Object)".)
     87    */
     88   private String asString(Method method) {
     89     String methodName = method.getName();
     90     Class<?>[] parameterTypes = method.getParameterTypes();
     91 
     92     if (parameterTypes.length == 0) {
     93       return methodName;
     94     }
     95 
     96     Iterable<String> parameterNames = Iterables.transform(
     97         Arrays.asList(parameterTypes),
     98         new Function<Class<?>, String>() {
     99           @Override
    100           public String apply(Class<?> from) {
    101             return from.getSimpleName();
    102           }
    103     });
    104     return methodName + "(" + COMMA_JOINER.join(parameterNames) + ")";
    105   }
    106 
    107   private static Object getDefaultValue(Class<?> returnType) {
    108     if (returnType == boolean.class || returnType == Boolean.class) {
    109       return Boolean.FALSE;
    110     } else if (returnType == int.class || returnType == Integer.class) {
    111       return 0;
    112     } else if ((returnType == Set.class) || (returnType == Collection.class)) {
    113       return Collections.emptySet();
    114     } else if (returnType == Iterator.class){
    115       return Iterators.emptyModifiableIterator();
    116     } else if (returnType.isArray()) {
    117       return Array.newInstance(returnType.getComponentType(), 0);
    118     } else {
    119       return null;
    120     }
    121   }
    122 
    123   protected static <T> void callAllPublicMethods(Class<T> theClass, T object)
    124       throws InvocationTargetException {
    125     for (Method method : theClass.getMethods()) {
    126       Class<?>[] parameterTypes = method.getParameterTypes();
    127       Object[] parameters = new Object[parameterTypes.length];
    128       for (int i = 0; i < parameterTypes.length; i++) {
    129         parameters[i] = getDefaultValue(parameterTypes[i]);
    130       }
    131       try {
    132         try {
    133           method.invoke(object, parameters);
    134         } catch (InvocationTargetException ex) {
    135           try {
    136             throw ex.getCause();
    137           } catch (UnsupportedOperationException unsupported) {
    138             // this is a legit exception
    139           }
    140         }
    141       } catch (Throwable cause) {
    142         throw new InvocationTargetException(cause,
    143             method.toString() + " with args: " + Arrays.toString(parameters));
    144       }
    145     }
    146   }
    147 }
    148