1 /* 2 * Copyright 2010 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy of 6 * 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, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations under 14 * the License. 15 */ 16 package com.google.android.testing.mocking; 17 18 import javassist.CannotCompileException; 19 import javassist.ClassPool; 20 import javassist.CtClass; 21 import javassist.CtMethod; 22 import javassist.NotFoundException; 23 24 import junit.framework.TestCase; 25 26 import java.io.ByteArrayInputStream; 27 import java.io.IOException; 28 import java.lang.reflect.Method; 29 import java.util.ArrayList; 30 import java.util.Arrays; 31 import java.util.HashMap; 32 import java.util.List; 33 import java.util.Map; 34 35 36 /** 37 * Various tests that verify that different types of Classes are handled 38 * correctly. 39 * 40 * @author swoodward (at) google.com (Stephen Woodward) 41 */ 42 public class ClassTypeTests extends TestCase { 43 private AndroidMockGenerator androidMockGenerator = new AndroidMockGenerator(); 44 45 private AndroidMockGenerator getAndroidMockGenerator() { 46 return androidMockGenerator; 47 } 48 49 private void assertAllMethodNames(List<String> expectedNames, 50 Map<String, List<String>> expectedMethods, List<GeneratedClassFile> classes) 51 throws IOException { 52 for (GeneratedClassFile clazz : classes) { 53 assertTrue(expectedNames.contains(clazz.getClassName())); 54 assertUnorderedContentsSame(expectedMethods.get(clazz.getClassName()), getMethodNames(clazz)); 55 } 56 } 57 58 private <T> void assertUnorderedContentsSame(Iterable<T> expected, Iterable<T> actual) { 59 List<T> missingItems = new ArrayList<T>(); 60 List<T> extraItems = new ArrayList<T>(); 61 for (T item : expected) { 62 missingItems.add(item); 63 } 64 for (T item : actual) { 65 missingItems.remove(item); 66 extraItems.add(item); 67 } 68 for (T item : expected) { 69 extraItems.remove(item); 70 } 71 if (missingItems.size() + extraItems.size() != 0) { 72 String errorMessage = 73 "Contents were different. Missing: " + Arrays.toString(missingItems.toArray()) 74 + " Extra: " + Arrays.toString(extraItems.toArray()); 75 fail(errorMessage); 76 } 77 } 78 79 private List<String> getExpectedNames(Class<?> clazz) { 80 return new ArrayList<String>(Arrays.asList(new String[] { 81 "genmocks." + clazz.getCanonicalName() + "DelegateInterface", 82 "genmocks." + clazz.getCanonicalName() + "DelegateSubclass"})); 83 } 84 85 private Iterable<String> getMethodNames(GeneratedClassFile clazz) throws IOException { 86 ByteArrayInputStream classInputStream = new ByteArrayInputStream(clazz.getContents()); 87 CtClass ctClass; 88 try { 89 ctClass = ClassPool.getDefault().getCtClass(clazz.getClassName()); 90 if (ctClass.isFrozen()) { 91 ctClass.defrost(); 92 } 93 } catch (NotFoundException e) { 94 // That's ok, we're just defrosting any classes that affect us that were created 95 // by other tests. NotFoundException implies the class is not frozen. 96 } 97 ctClass = ClassPool.getDefault().makeClass(classInputStream); 98 return getMethodNames(ctClass.getDeclaredMethods()); 99 } 100 101 private List<String> getMethodNames(CtMethod[] methods) { 102 List<String> methodNames = new ArrayList<String>(); 103 for (CtMethod method : methods) { 104 methodNames.add(method.getName()); 105 } 106 return methodNames; 107 } 108 109 private List<String> getMethodNames(Method[] methods, String[] exclusions) { 110 List<String> methodNames = new ArrayList<String>(); 111 for (Method method : methods) { 112 if (!Arrays.asList(exclusions).contains(method.getName())) { 113 methodNames.add(method.getName()); 114 } 115 } 116 return methodNames; 117 } 118 119 private Map<String, List<String>> getExpectedMethodsMap(List<String> expectedNames, 120 Class<?> clazz) { 121 return getExpectedMethodsMap(expectedNames, clazz, new String[0]); 122 } 123 124 private Map<String, List<String>> getExpectedMethodsMap(List<String> expectedNames, 125 Class<?> clazz, String[] exclusions) { 126 Map<String, List<String>> expectedMethods = new HashMap<String, List<String>>(); 127 expectedMethods.put(expectedNames.get(0), new ArrayList<String>(Arrays.asList(new String[] { 128 "finalize", "clone"}))); 129 expectedMethods.put(expectedNames.get(1), new ArrayList<String>(Arrays.asList(new String[] { 130 "finalize", "clone", "setDelegate___AndroidMock", "getDelegate___AndroidMock"}))); 131 expectedMethods.get(expectedNames.get(0)).addAll( 132 getMethodNames(clazz.getDeclaredMethods(), exclusions)); 133 expectedMethods.get(expectedNames.get(1)).addAll( 134 getMethodNames(clazz.getDeclaredMethods(), exclusions)); 135 return expectedMethods; 136 } 137 138 public void testClassIsDuplicate() throws ClassNotFoundException, IOException, 139 CannotCompileException { 140 List<GeneratedClassFile> classList = 141 getAndroidMockGenerator().createMocksForClass(Object.class); 142 List<GeneratedClassFile> secondClassList = 143 getAndroidMockGenerator().createMocksForClass(Object.class); 144 assertEquals(classList, secondClassList); 145 } 146 147 public void testClassHasDelegateMethods() throws ClassNotFoundException, IOException, 148 CannotCompileException { 149 List<String> expectedNames = getExpectedNames(ClassHasDelegateMethods.class); 150 Map<String, List<String>> expectedMethods = 151 getExpectedMethodsMap(expectedNames, ClassHasDelegateMethods.class, 152 new String[] {"getDelegate___AndroidMock"}); 153 // This use case doesn't fit our util in any nice way, so just tweak it. 154 expectedMethods.get( 155 "genmocks.com.google.android.testing.mocking.ClassHasDelegateMethodsDelegateInterface") 156 .add("getDelegate___AndroidMock"); 157 158 AndroidMockGenerator mockGenerator = getAndroidMockGenerator(); 159 List<GeneratedClassFile> classes = 160 mockGenerator.createMocksForClass(ClassHasDelegateMethods.class); 161 assertEquals(2, classes.size()); 162 assertAllMethodNames(expectedNames, expectedMethods, classes); 163 } 164 165 public void testClassHasFinalMethods() throws ClassNotFoundException, IOException, 166 CannotCompileException { 167 List<String> expectedNames = getExpectedNames(ClassHasFinalMethods.class); 168 Map<String, List<String>> expectedMethods = 169 getExpectedMethodsMap(expectedNames, ClassHasFinalMethods.class, new String[] {"foo", 170 "foobar"}); 171 172 AndroidMockGenerator mockGenerator = getAndroidMockGenerator(); 173 List<GeneratedClassFile> classes = 174 mockGenerator.createMocksForClass(ClassHasFinalMethods.class); 175 assertEquals(2, classes.size()); 176 assertAllMethodNames(expectedNames, expectedMethods, classes); 177 } 178 179 public void testClassHasNoDefaultConstructor() throws ClassNotFoundException, IOException, 180 CannotCompileException { 181 AndroidMockGenerator mockGenerator = getAndroidMockGenerator(); 182 List<GeneratedClassFile> classes = 183 mockGenerator.createMocksForClass(ClassHasNoDefaultConstructor.class); 184 assertEquals(2, classes.size()); 185 } 186 187 public void testClassHasNoPublicConstructors() throws ClassNotFoundException, IOException, 188 CannotCompileException { 189 AndroidMockGenerator mockGenerator = getAndroidMockGenerator(); 190 List<GeneratedClassFile> classes = 191 mockGenerator.createMocksForClass(ClassHasNoPublicConstructors.class); 192 assertEquals(0, classes.size()); 193 } 194 195 public void testClassHasOverloadedMethods() throws ClassNotFoundException, IOException, 196 CannotCompileException { 197 List<String> expectedNames = getExpectedNames(ClassHasOverloadedMethods.class); 198 Map<String, List<String>> expectedMethods = 199 getExpectedMethodsMap(expectedNames, ClassHasOverloadedMethods.class); 200 201 AndroidMockGenerator mockGenerator = getAndroidMockGenerator(); 202 List<GeneratedClassFile> classes = 203 mockGenerator.createMocksForClass(ClassHasOverloadedMethods.class); 204 assertEquals(2, classes.size()); 205 assertAllMethodNames(expectedNames, expectedMethods, classes); 206 } 207 208 public void testClassHasStaticMethods() throws ClassNotFoundException, IOException, 209 CannotCompileException { 210 List<String> expectedNames = getExpectedNames(ClassHasStaticMethods.class); 211 Map<String, List<String>> expectedMethods = 212 getExpectedMethodsMap(expectedNames, ClassHasStaticMethods.class, 213 new String[] {"staticFoo"}); 214 215 AndroidMockGenerator mockGenerator = getAndroidMockGenerator(); 216 List<GeneratedClassFile> classes = 217 mockGenerator.createMocksForClass(ClassHasStaticMethods.class); 218 assertEquals(2, classes.size()); 219 assertAllMethodNames(expectedNames, expectedMethods, classes); 220 } 221 222 public void testClassIsAnnotation() throws ClassNotFoundException, IOException, 223 CannotCompileException { 224 AndroidMockGenerator mockGenerator = getAndroidMockGenerator(); 225 List<GeneratedClassFile> classes = mockGenerator.createMocksForClass(ClassIsAnnotation.class); 226 assertEquals(0, classes.size()); 227 } 228 229 public void testClassIsEnum() throws ClassNotFoundException, IOException, CannotCompileException { 230 AndroidMockGenerator mockGenerator = getAndroidMockGenerator(); 231 List<GeneratedClassFile> classes = mockGenerator.createMocksForClass(ClassIsEnum.class); 232 assertEquals(0, classes.size()); 233 } 234 235 public void testClassIsFinal() throws ClassNotFoundException, IOException, 236 CannotCompileException { 237 AndroidMockGenerator mockGenerator = getAndroidMockGenerator(); 238 List<GeneratedClassFile> classes = mockGenerator.createMocksForClass(ClassIsFinal.class); 239 assertEquals(0, classes.size()); 240 } 241 242 public void testClassIsInterface() throws ClassNotFoundException, IOException, 243 CannotCompileException { 244 AndroidMockGenerator mockGenerator = getAndroidMockGenerator(); 245 List<GeneratedClassFile> classes = mockGenerator.createMocksForClass(ClassIsInterface.class); 246 assertEquals(0, classes.size()); 247 } 248 249 public void testClassIsArray() throws ClassNotFoundException, IOException, 250 CannotCompileException { 251 AndroidMockGenerator mockGenerator = getAndroidMockGenerator(); 252 List<GeneratedClassFile> classes = mockGenerator.createMocksForClass(Object[].class); 253 assertEquals(0, classes.size()); 254 } 255 256 public void testClassIsNormal() throws ClassNotFoundException, IOException, 257 CannotCompileException { 258 AndroidMockGenerator mockGenerator = getAndroidMockGenerator(); 259 List<GeneratedClassFile> classes = mockGenerator.createMocksForClass(Object.class); 260 assertEquals(2, classes.size()); 261 } 262 263 public void testClassIsPrimitive() throws ClassNotFoundException, IOException, 264 CannotCompileException { 265 AndroidMockGenerator mockGenerator = getAndroidMockGenerator(); 266 List<GeneratedClassFile> classes = mockGenerator.createMocksForClass(Integer.TYPE); 267 assertEquals(0, classes.size()); 268 } 269 } 270