• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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