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