Home | History | Annotate | Download | only in browser
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 package org.chromium.content.browser;
      6 
      7 import android.test.InstrumentationTestCase;
      8 import android.test.suitebuilder.annotation.SmallTest;
      9 
     10 import org.chromium.base.test.util.Feature;
     11 import org.chromium.content.browser.DownloadInfo.Builder;
     12 
     13 import java.lang.reflect.InvocationTargetException;
     14 import java.lang.reflect.Method;
     15 import java.util.HashMap;
     16 import java.util.Random;
     17 
     18 /**
     19  * Tests for (@link DownloadInfo}.
     20  */
     21 public class DownloadInfoTest extends InstrumentationTestCase {
     22     /**
     23      * Class to store getter/setter information. Stores the method name of a getter and setter
     24      * without prefix, so "int getFoo()" and "void setFoo(int)" both have same AccessorSignature
     25      * {"Foo", int}.
     26      */
     27     static class AccessorSignature {
     28         final String mMethodNameWithoutPrefix;
     29         final Class<?> mReturnTypeOrParam;
     30 
     31         AccessorSignature(String methodNameWithoutPrefix, Class<?> returnTypeOrParam) {
     32             mMethodNameWithoutPrefix = methodNameWithoutPrefix;
     33             mReturnTypeOrParam = returnTypeOrParam;
     34         }
     35 
     36         @Override
     37         public int hashCode() {
     38             return mMethodNameWithoutPrefix.hashCode() * 31
     39                     + mReturnTypeOrParam.hashCode();
     40         }
     41 
     42         @Override
     43         public boolean equals(Object obj) {
     44             if (obj == this) {
     45                 return true;
     46             }
     47             if (obj instanceof AccessorSignature) {
     48                 AccessorSignature other = (AccessorSignature) obj;
     49                 return other.mReturnTypeOrParam == mReturnTypeOrParam
     50                        && other.mMethodNameWithoutPrefix.equals(mMethodNameWithoutPrefix);
     51             }
     52             return false;
     53         }
     54 
     55         @Override
     56         public String toString() {
     57             return "{ " + mMethodNameWithoutPrefix + ", " + mReturnTypeOrParam.getName() + "}";
     58         }
     59     }
     60 
     61     /**
     62      * Returns an AccessorInfo object for the method if it is a getter. A getter for the class has
     63      * signature: Type getFoo(), where Type is a String or a primitive type other than boolean.
     64      * Boolean getters are an exception and have special prefixes. In case of a boolean getter, the
     65      * signature is 'boolean isFoo()' or 'boolean hasFoo', i.e. boolean getters start with "is" or
     66      * "has".
     67      * @param method the method that is a getter
     68      * @return AccessorInfo for the method if it is a getter, null otherwise.
     69      */
     70     AccessorSignature getGetterInfo(Method method) {
     71         // A getter is of format Type getFoo(): i.e. 0 params and one
     72         // return type.
     73         if (method.getParameterTypes().length == 0) {
     74             // Based on return type extract the name of the getter.
     75             Class<?> returnType = method.getReturnType();
     76             if (returnType.isPrimitive() || returnType == String.class) {
     77                 String methodName = method.getName();
     78                 if (returnType.equals(Boolean.TYPE)) {
     79                     if (methodName.matches("(is|has).*")) {
     80                         return new AccessorSignature(methodName.replaceFirst("is|has", ""),
     81                                 returnType);
     82                     }
     83                 } else {
     84                     if (methodName.startsWith("get")) {
     85                         return new AccessorSignature(methodName.substring(3), returnType);
     86                     }
     87                 }
     88             }
     89         }
     90         return null;
     91     }
     92 
     93     /**
     94      * Returns an AccessorInfo object for the method if it is a setter. A setter for the class has
     95      * signature: Type setFoo(), where Type is a String or a primitive type.
     96      * @param method the method that is a getter
     97      * @return AccessorInfo for the method if it is a getter, null otherwise.
     98      */
     99     AccessorSignature getSetterInfo(Method method) {
    100         if (method.getParameterTypes().length == 1) {
    101             Class<?> parameter = method.getParameterTypes()[0];
    102             String methodName = method.getName();
    103             if (methodName.startsWith("set")) {
    104                 if (parameter.equals(Boolean.TYPE)) {
    105                     // Boolean setters are of form setIsFoo or setHasFoo.
    106                     return new AccessorSignature(
    107                             methodName.replaceFirst("set(Is|Has)", ""),
    108                             parameter);
    109                 } else {
    110                     return new AccessorSignature(methodName.substring(3), parameter);
    111                 }
    112             }
    113         }
    114         return null;
    115     }
    116 
    117     /**
    118      * Invoke a method via reflection and rethrow the exception. Makes findbugs happy.
    119      * @param method Method to invoke.
    120      * @param instance class instance on which method should be invoked.
    121      * @return return value of invocation.
    122      */
    123     Object invokeMethod(Method method, Object instance, Object... args) throws Exception {
    124         try {
    125             return method.invoke(instance, args);
    126         } catch (IllegalArgumentException e) {
    127             throw e;
    128         } catch (IllegalAccessException e) {
    129             throw e;
    130         } catch (InvocationTargetException e) {
    131             throw e;
    132         }
    133     }
    134 
    135     @SmallTest
    136     @Feature({"Downloads"})
    137     public void testBuilderHasCorrectSetters() {
    138         HashMap<AccessorSignature, Method> downloadInfoGetters =
    139                 new HashMap<AccessorSignature, Method>();
    140         HashMap<AccessorSignature, Method> builderSetters =
    141                 new HashMap<AccessorSignature, Method>();
    142         for (Method m : DownloadInfo.class.getMethods()) {
    143             AccessorSignature info = getGetterInfo(m);
    144             if (info != null) {
    145                 downloadInfoGetters.put(info, m);
    146             }
    147         }
    148         assertTrue("There should be at least one getter.",
    149                 downloadInfoGetters.size() > 0);
    150         for (Method m : Builder.class.getMethods()) {
    151             AccessorSignature info = getSetterInfo(m);
    152             if (info != null) {
    153                 builderSetters.put(info, m);
    154             }
    155         }
    156 
    157         // Make sure we have a setter for each getter and vice versa.
    158         assertEquals("Mismatch between getters and setters.",
    159                 downloadInfoGetters.keySet(), builderSetters.keySet());
    160 
    161         // Generate specific values for fields and verify that they are correctly set. For boolean
    162         // fields set them all to true. For integers generate random numbers.
    163         Random random = new Random();
    164         HashMap<AccessorSignature, Object> valuesForBuilder =
    165                 new HashMap<DownloadInfoTest.AccessorSignature, Object>();
    166         for (AccessorSignature signature : builderSetters.keySet()) {
    167             if (signature.mReturnTypeOrParam.equals(String.class)) {
    168                 String value = signature.mMethodNameWithoutPrefix
    169                         + Integer.toString(random.nextInt());
    170                 valuesForBuilder.put(signature, value);
    171             } else if (signature.mReturnTypeOrParam.equals(Boolean.TYPE)) {
    172                 valuesForBuilder.put(signature, Boolean.TRUE);
    173             } else {
    174                 // This is a primitive type that is not boolean, probably an integer.
    175                 valuesForBuilder.put(signature, Integer.valueOf(random.nextInt(100)));
    176             }
    177         }
    178 
    179         Builder builder = new Builder();
    180         // Create a DownloadInfo object with these values.
    181         for (AccessorSignature signature : builderSetters.keySet()) {
    182             Method setter = builderSetters.get(signature);
    183             try {
    184                 invokeMethod(setter, builder, valuesForBuilder.get(signature));
    185             } catch (Exception e) {
    186                 fail("Exception while setting value in the setter. Signature: " + signature
    187                         + " value:" + valuesForBuilder.get(signature) + ":" + e);
    188             }
    189         }
    190         DownloadInfo downloadInfo = builder.build();
    191         for (AccessorSignature signature : downloadInfoGetters.keySet()) {
    192             Method getter = downloadInfoGetters.get(signature);
    193             try {
    194                 Object returnValue = invokeMethod(getter, downloadInfo);
    195                 assertEquals(signature.toString(),
    196                         valuesForBuilder.get(signature).toString(), returnValue.toString());
    197             } catch (Exception e) {
    198                 fail("Exception while getting value from getter. Signature: " + signature
    199                         + " value:" + valuesForBuilder.get(signature));
    200             }
    201         }
    202 
    203         // Test DownloadInfo.fromDownloadInfo copies all fields.
    204         DownloadInfo newDownloadInfo = Builder.fromDownloadInfo(downloadInfo).build();
    205         for (AccessorSignature signature : downloadInfoGetters.keySet()) {
    206             Method getter = downloadInfoGetters.get(signature);
    207             try {
    208                 Object returnValue1 = invokeMethod(getter, downloadInfo);
    209                 Object returnValue2 = invokeMethod(getter, newDownloadInfo);
    210                 assertEquals(signature.toString(), returnValue1, returnValue2);
    211             } catch (Exception e) {
    212                 fail("Exception while getting value from getter. Signature: " + signature
    213                         + " value:" + valuesForBuilder.get(signature) + ":" + e);
    214             }
    215         }
    216     }
    217 }
    218