1 /* 2 * Copyright (C) 2006 Google Inc. 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.inject; 18 19 import static com.google.inject.Asserts.assertContains; 20 import static com.google.inject.Asserts.assertEqualsBothWays; 21 import static com.google.inject.Asserts.assertNotSerializable; 22 import static com.google.inject.Asserts.awaitClear; 23 import static java.lang.annotation.RetentionPolicy.RUNTIME; 24 25 import com.google.inject.name.Named; 26 import com.google.inject.name.Names; 27 import com.google.inject.spi.Dependency; 28 import com.google.inject.util.Types; 29 import java.io.IOException; 30 import java.lang.annotation.ElementType; 31 import java.lang.annotation.Retention; 32 import java.lang.annotation.Target; 33 import java.lang.ref.WeakReference; 34 import java.lang.reflect.Method; 35 import java.lang.reflect.ParameterizedType; 36 import java.lang.reflect.Type; 37 import java.lang.reflect.TypeVariable; 38 import java.util.ArrayList; 39 import java.util.List; 40 import java.util.Map; 41 import java.util.concurrent.atomic.AtomicReference; 42 import junit.framework.TestCase; 43 44 /** @author crazybob@google.com (Bob Lee) */ 45 public class KeyTest extends TestCase { 46 foo(List<String> a, List<String> b)47 public void foo(List<String> a, List<String> b) {} 48 bar(Provider<List<String>> a)49 public void bar(Provider<List<String>> a) {} 50 51 @Foo String baz; 52 List<? extends CharSequence> wildcardExtends; 53 testOfType()54 public void testOfType() { 55 Key<Object> k = Key.get(Object.class, Foo.class); 56 Key<Integer> ki = k.ofType(Integer.class); 57 assertEquals(Integer.class, ki.getRawType()); 58 assertEquals(Foo.class, ki.getAnnotationType()); 59 } 60 testKeyEquality()61 public void testKeyEquality() { 62 Key<List<String>> a = new Key<List<String>>(Foo.class) {}; 63 Key<List<String>> b = Key.get(new TypeLiteral<List<String>>() {}, Foo.class); 64 assertEqualsBothWays(a, b); 65 } 66 testProviderKey()67 public void testProviderKey() throws NoSuchMethodException { 68 Key<?> actual = 69 Key.get(getClass().getMethod("foo", List.class, List.class).getGenericParameterTypes()[0]) 70 .providerKey(); 71 Key<?> expected = 72 Key.get(getClass().getMethod("bar", Provider.class).getGenericParameterTypes()[0]); 73 assertEqualsBothWays(expected, actual); 74 assertEquals(expected.toString(), actual.toString()); 75 } 76 testTypeEquality()77 public void testTypeEquality() throws Exception { 78 Method m = getClass().getMethod("foo", List.class, List.class); 79 Type[] types = m.getGenericParameterTypes(); 80 assertEquals(types[0], types[1]); 81 Key<List<String>> k = new Key<List<String>>() {}; 82 assertEquals(types[0], k.getTypeLiteral().getType()); 83 assertFalse(types[0].equals(new Key<List<Integer>>() {}.getTypeLiteral().getType())); 84 } 85 86 /** 87 * Key canonicalizes {@link int.class} to {@code Integer.class}, and won't expose wrapper types. 88 */ testPrimitivesAndWrappersAreEqual()89 public void testPrimitivesAndWrappersAreEqual() { 90 Class[] primitives = 91 new Class[] { 92 boolean.class, 93 byte.class, 94 short.class, 95 int.class, 96 long.class, 97 float.class, 98 double.class, 99 char.class, 100 void.class 101 }; 102 Class[] wrappers = 103 new Class[] { 104 Boolean.class, 105 Byte.class, 106 Short.class, 107 Integer.class, 108 Long.class, 109 Float.class, 110 Double.class, 111 Character.class, 112 Void.class 113 }; 114 115 for (int t = 0; t < primitives.length; t++) { 116 @SuppressWarnings("unchecked") 117 Key primitiveKey = Key.get(primitives[t]); 118 @SuppressWarnings("unchecked") 119 Key wrapperKey = Key.get(wrappers[t]); 120 121 assertEquals(primitiveKey, wrapperKey); 122 assertEquals(wrappers[t], primitiveKey.getRawType()); 123 assertEquals(wrappers[t], wrapperKey.getRawType()); 124 assertEquals(wrappers[t], primitiveKey.getTypeLiteral().getType()); 125 assertEquals(wrappers[t], wrapperKey.getTypeLiteral().getType()); 126 } 127 128 Key<Integer> integerKey = Key.get(Integer.class); 129 Key<Integer> integerKey2 = Key.get(Integer.class, Named.class); 130 Key<Integer> integerKey3 = Key.get(Integer.class, Names.named("int")); 131 132 Class<Integer> intClassLiteral = int.class; 133 assertEquals(integerKey, Key.get(intClassLiteral)); 134 assertEquals(integerKey2, Key.get(intClassLiteral, Named.class)); 135 assertEquals(integerKey3, Key.get(intClassLiteral, Names.named("int"))); 136 137 Type intType = int.class; 138 assertEquals(integerKey, Key.get(intType)); 139 assertEquals(integerKey2, Key.get(intType, Named.class)); 140 assertEquals(integerKey3, Key.get(intType, Names.named("int"))); 141 142 TypeLiteral<Integer> intTypeLiteral = TypeLiteral.get(int.class); 143 assertEquals(integerKey, Key.get(intTypeLiteral)); 144 assertEquals(integerKey2, Key.get(intTypeLiteral, Named.class)); 145 assertEquals(integerKey3, Key.get(intTypeLiteral, Names.named("int"))); 146 } 147 testSerialization()148 public void testSerialization() throws IOException, NoSuchFieldException { 149 assertNotSerializable(Key.get(B.class)); 150 assertNotSerializable(Key.get(B.class, Names.named("bee"))); 151 assertNotSerializable(Key.get(B.class, Named.class)); 152 assertNotSerializable(Key.get(B[].class)); 153 assertNotSerializable(Key.get(new TypeLiteral<Map<List<B>, B>>() {})); 154 assertNotSerializable(Key.get(new TypeLiteral<List<B[]>>() {})); 155 assertNotSerializable(Key.get(Types.listOf(Types.subtypeOf(CharSequence.class)))); 156 } 157 testEqualityOfAnnotationTypesAndInstances()158 public void testEqualityOfAnnotationTypesAndInstances() throws NoSuchFieldException { 159 Foo instance = getClass().getDeclaredField("baz").getAnnotation(Foo.class); 160 Key<String> keyWithInstance = Key.get(String.class, instance); 161 Key<String> keyWithLiteral = Key.get(String.class, Foo.class); 162 assertEqualsBothWays(keyWithInstance, keyWithLiteral); 163 } 164 testNonBindingAnnotationOnKey()165 public void testNonBindingAnnotationOnKey() { 166 try { 167 Key.get(String.class, Deprecated.class); 168 fail(); 169 } catch (IllegalArgumentException expected) { 170 assertContains( 171 expected.getMessage(), 172 "java.lang.Deprecated is not a binding annotation. ", 173 "Please annotate it with @BindingAnnotation."); 174 } 175 } 176 testBindingAnnotationWithoutRuntimeRetention()177 public void testBindingAnnotationWithoutRuntimeRetention() { 178 try { 179 Key.get(String.class, Bar.class); 180 fail(); 181 } catch (IllegalArgumentException expected) { 182 assertContains( 183 expected.getMessage(), 184 Bar.class.getName() + " is not retained at runtime.", 185 "Please annotate it with @Retention(RUNTIME)."); 186 } 187 } 188 parameterizedWithVariable(List<T> typeWithVariables)189 <T> void parameterizedWithVariable(List<T> typeWithVariables) {} 190 191 /** Test for issue 186 */ testCannotCreateKeysWithTypeVariables()192 public void testCannotCreateKeysWithTypeVariables() throws NoSuchMethodException { 193 ParameterizedType listOfTType = 194 (ParameterizedType) 195 getClass() 196 .getDeclaredMethod("parameterizedWithVariable", List.class) 197 .getGenericParameterTypes()[ 198 0]; 199 200 TypeLiteral<?> listOfT = TypeLiteral.get(listOfTType); 201 try { 202 Key.get(listOfT); 203 fail("Guice should not allow keys for java.util.List<T>"); 204 } catch (ConfigurationException e) { 205 assertContains( 206 e.getMessage(), "java.util.List<T> cannot be used as a key; It is not fully specified."); 207 } 208 209 TypeVariable tType = (TypeVariable) listOfTType.getActualTypeArguments()[0]; 210 TypeLiteral<?> t = TypeLiteral.get(tType); 211 try { 212 Key.get(t); 213 fail("Guice should not allow keys for T"); 214 } catch (ConfigurationException e) { 215 assertContains(e.getMessage(), "T cannot be used as a key; It is not fully specified."); 216 } 217 } 218 testCannotGetKeyWithUnspecifiedTypeVariables()219 public void testCannotGetKeyWithUnspecifiedTypeVariables() { 220 TypeLiteral<Integer> typeLiteral = KeyTest.createTypeLiteral(); 221 try { 222 Key.get(typeLiteral); 223 fail("Guice should not allow keys for T"); 224 } catch (ConfigurationException e) { 225 assertContains(e.getMessage(), "T cannot be used as a key; It is not fully specified."); 226 } 227 } 228 createTypeLiteral()229 private static <T> TypeLiteral<T> createTypeLiteral() { 230 return new TypeLiteral<T>() {}; 231 } 232 testCannotCreateKeySubclassesWithUnspecifiedTypeVariables()233 public void testCannotCreateKeySubclassesWithUnspecifiedTypeVariables() { 234 try { 235 KeyTest.<Integer>createKey(); 236 fail("Guice should not allow keys for T"); 237 } catch (ConfigurationException e) { 238 assertContains(e.getMessage(), "T cannot be used as a key; It is not fully specified."); 239 } 240 } 241 createKey()242 private static <T> Key<T> createKey() { 243 return new Key<T>() {}; 244 } 245 246 interface B {} 247 248 @Retention(RUNTIME) 249 @Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD}) 250 @BindingAnnotation 251 @interface Foo {} 252 253 @Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD}) 254 @BindingAnnotation 255 @interface Bar {} 256 257 static class HasTypeParameters<A, B extends List<A> & Runnable, C extends Runnable> { 258 A a; 259 B b; 260 C c; 261 } 262 testKeysWithDefaultAnnotations()263 public void testKeysWithDefaultAnnotations() { 264 AllDefaults allDefaults = HasAnnotations.class.getAnnotation(AllDefaults.class); 265 assertEquals(Key.get(Foo.class, allDefaults), Key.get(Foo.class, AllDefaults.class)); 266 267 Marker marker = HasAnnotations.class.getAnnotation(Marker.class); 268 assertEquals(Key.get(Foo.class, marker), Key.get(Foo.class, Marker.class)); 269 270 Key<?> noDefaults = Key.get(Foo.class, NoDefaults.class); 271 assertNull(noDefaults.getAnnotation()); 272 assertEquals(NoDefaults.class, noDefaults.getAnnotationType()); 273 274 Key<?> someDefaults = Key.get(Foo.class, SomeDefaults.class); 275 assertNull(someDefaults.getAnnotation()); 276 assertEquals(SomeDefaults.class, someDefaults.getAnnotationType()); 277 } 278 279 @Retention(RUNTIME) 280 @BindingAnnotation 281 @interface AllDefaults { v1()282 int v1() default 1; 283 v2()284 String v2() default "foo"; 285 } 286 287 @Retention(RUNTIME) 288 @BindingAnnotation 289 @interface SomeDefaults { v1()290 int v1() default 1; 291 v2()292 String v2() default "foo"; 293 clazz()294 Class<?> clazz(); 295 } 296 297 @Retention(RUNTIME) 298 @BindingAnnotation 299 @interface NoDefaults { value()300 int value(); 301 } 302 303 @Retention(RUNTIME) 304 @BindingAnnotation 305 @interface Marker {} 306 307 @AllDefaults 308 @Marker 309 static class HasAnnotations {} 310 testAnonymousClassesDontHoldRefs()311 public void testAnonymousClassesDontHoldRefs() { 312 final AtomicReference<Provider<List<String>>> stringProvider = 313 new AtomicReference<Provider<List<String>>>(); 314 final AtomicReference<Provider<List<Integer>>> intProvider = 315 new AtomicReference<Provider<List<Integer>>>(); 316 final Object foo = 317 new Object() { 318 @SuppressWarnings("unused") 319 @Inject 320 List<String> list; 321 }; 322 Module module = 323 new AbstractModule() { 324 @Override 325 protected void configure() { 326 bind(new Key<List<String>>() {}).toInstance(new ArrayList<String>()); 327 bind(new TypeLiteral<List<Integer>>() {}).toInstance(new ArrayList<Integer>()); 328 329 stringProvider.set(getProvider(new Key<List<String>>() {})); 330 intProvider.set(binder().getProvider(Dependency.get(new Key<List<Integer>>() {}))); 331 332 binder().requestInjection(new TypeLiteral<Object>() {}, foo); 333 } 334 }; 335 WeakReference<Module> moduleRef = new WeakReference<>(module); 336 final Injector injector = Guice.createInjector(module); 337 module = null; 338 awaitClear(moduleRef); // Make sure anonymous keys & typeliterals don't hold the module. 339 340 Runnable runner = 341 new Runnable() { 342 @Override 343 public void run() { 344 injector.getInstance(new Key<Typed<String>>() {}); 345 injector.getInstance(Key.get(new TypeLiteral<Typed<Integer>>() {})); 346 } 347 }; 348 WeakReference<Runnable> runnerRef = new WeakReference<>(runner); 349 runner.run(); 350 runner = null; 351 awaitClear(runnerRef); // also make sure anonymous keys & typeliterals don't hold for JITs 352 } 353 354 static class Typed<T> {} 355 } 356