1 /**
2  * Copyright (C) 2008 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.spi;
18 
19 import static com.google.inject.Asserts.assertContains;
20 import static com.google.inject.Asserts.getDeclaringSourcePart;
21 import static com.google.inject.Asserts.isIncludeStackTraceComplete;
22 
23 import com.google.common.collect.ImmutableSet;
24 import com.google.common.collect.Lists;
25 import com.google.inject.AbstractModule;
26 import com.google.inject.Binding;
27 import com.google.inject.Guice;
28 import com.google.inject.Inject;
29 import com.google.inject.Injector;
30 import com.google.inject.Key;
31 import com.google.inject.Module;
32 import com.google.inject.Provider;
33 import com.google.inject.Scope;
34 import com.google.inject.Scopes;
35 import com.google.inject.Singleton;
36 import com.google.inject.Stage;
37 import com.google.inject.name.Names;
38 
39 import junit.framework.AssertionFailedError;
40 import junit.framework.TestCase;
41 
42 import java.lang.reflect.Constructor;
43 import java.util.Collections;
44 import java.util.Comparator;
45 import java.util.Iterator;
46 import java.util.List;
47 import java.util.concurrent.atomic.AtomicBoolean;
48 import java.util.logging.Logger;
49 
50 /**
51  * @author jessewilson@google.com (Jesse Wilson)
52  */
53 public class SpiBindingsTest extends TestCase {
54 
testBindConstant()55   public void testBindConstant() {
56     checkInjector(
57         new AbstractModule() {
58           protected void configure() {
59             bindConstant().annotatedWith(Names.named("one")).to(1);
60           }
61         },
62 
63         new FailingElementVisitor() {
64           @Override public <T> Void visit(Binding<T> binding) {
65             assertTrue(binding instanceof InstanceBinding);
66             assertEquals(Key.get(Integer.class, Names.named("one")), binding.getKey());
67             return null;
68           }
69         }
70     );
71   }
72 
testToInstanceBinding()73   public void testToInstanceBinding() {
74     checkInjector(
75         new AbstractModule() {
76           protected void configure() {
77             bind(String.class).toInstance("A");
78           }
79         },
80 
81         new FailingElementVisitor() {
82           @Override public <T> Void visit(Binding<T> binding) {
83             assertTrue(binding instanceof InstanceBinding);
84             checkBindingSource(binding);
85             assertEquals(Key.get(String.class), binding.getKey());
86             binding.acceptTargetVisitor(new FailingTargetVisitor<T>() {
87               @Override public Void visit(InstanceBinding<? extends T> binding) {
88                 assertEquals("A", binding.getInstance());
89                 return null;
90               }
91             });
92             binding.acceptScopingVisitor(new FailingBindingScopingVisitor() {
93               public Void visitEagerSingleton() {
94                 return null;
95               }
96             });
97             return null;
98           }
99         }
100     );
101   }
102 
testToProviderBinding()103   public void testToProviderBinding() {
104     final Provider<String> stringProvider = new StringProvider();
105 
106     checkInjector(
107         new AbstractModule() {
108           protected void configure() {
109             bind(String.class).toProvider(stringProvider);
110           }
111         },
112 
113         new FailingElementVisitor() {
114           @Override public <T> Void visit(Binding<T> binding) {
115             assertTrue(binding instanceof ProviderInstanceBinding);
116             checkBindingSource(binding);
117             assertEquals(Key.get(String.class), binding.getKey());
118             binding.acceptTargetVisitor(new FailingTargetVisitor<T>() {
119               @Override public Void visit(
120                   ProviderInstanceBinding<? extends T> binding) {
121                 assertSame(stringProvider, binding.getUserSuppliedProvider());
122                 return null;
123               }
124             });
125             return null;
126           }
127         }
128     );
129   }
130 
testToProviderKeyBinding()131   public void testToProviderKeyBinding() {
132     checkInjector(
133         new AbstractModule() {
134           protected void configure() {
135             bind(String.class).toProvider(StringProvider.class);
136           }
137         },
138 
139         new FailingElementVisitor() {
140           @Override public <T> Void visit(Binding<T> binding) {
141             assertTrue(binding instanceof ProviderKeyBinding);
142             checkBindingSource(binding);
143             assertEquals(Key.get(String.class), binding.getKey());
144             binding.acceptTargetVisitor(new FailingTargetVisitor<T>() {
145               @Override public Void visit(ProviderKeyBinding<? extends T> binding) {
146                 assertEquals(Key.get(StringProvider.class), binding.getProviderKey());
147                 return null;
148               }
149             });
150             return null;
151           }
152         }
153     );
154   }
155 
testToKeyBinding()156   public void testToKeyBinding() {
157     final Key<String> aKey = Key.get(String.class, Names.named("a"));
158     final Key<String> bKey = Key.get(String.class, Names.named("b"));
159 
160     checkInjector(
161         new AbstractModule() {
162           protected void configure() {
163             bind(aKey).to(bKey);
164             bind(bKey).toInstance("B");
165           }
166         },
167 
168         new FailingElementVisitor() {
169           @Override public <T> Void visit(Binding<T> binding) {
170             assertTrue(binding instanceof LinkedKeyBinding);
171             checkBindingSource(binding);
172             assertEquals(aKey, binding.getKey());
173             binding.acceptTargetVisitor(new FailingTargetVisitor<T>() {
174               @Override public Void visit(LinkedKeyBinding<? extends T> binding) {
175                 assertEquals(bKey, binding.getLinkedKey());
176                 return null;
177               }
178             });
179             return null;
180           }
181         },
182 
183         new FailingElementVisitor() {
184           @Override public <T> Void visit(Binding<T> binding) {
185             assertEquals(bKey, binding.getKey());
186             return null;
187           }
188         }
189     );
190   }
191 
testToConstructorBinding()192   public void testToConstructorBinding() {
193     checkInjector(
194         new AbstractModule() {
195           protected void configure() {
196             bind(D.class);
197           }
198         },
199 
200         new FailingElementVisitor() {
201           @Override public <T> Void visit(Binding<T> binding) {
202             assertTrue(binding instanceof ConstructorBinding);
203             checkBindingSource(binding);
204             assertEquals(Key.get(D.class), binding.getKey());
205             binding.acceptTargetVisitor(new FailingTargetVisitor<T>() {
206               @Override public Void visit(ConstructorBinding<? extends T> binding) {
207                 Constructor<?> expected = D.class.getDeclaredConstructors()[0];
208                 assertEquals(expected, binding.getConstructor().getMember());
209                 assertEquals(ImmutableSet.<InjectionPoint>of(), binding.getInjectableMembers());
210                 return null;
211               }
212             });
213             return null;
214           }
215         }
216     );
217   }
218 
testConstantBinding()219   public void testConstantBinding() {
220     checkInjector(
221         new AbstractModule() {
222           protected void configure() {
223             bindConstant().annotatedWith(Names.named("one")).to(1);
224           }
225         },
226 
227         new FailingElementVisitor() {
228           @Override public <T> Void visit(Binding<T> binding) {
229             assertTrue(binding instanceof InstanceBinding);
230             checkBindingSource(binding);
231             assertEquals(Key.get(Integer.class, Names.named("one")), binding.getKey());
232             binding.acceptTargetVisitor(new FailingTargetVisitor<T>() {
233               @Override public Void visit(InstanceBinding<? extends T> binding) {
234                 assertEquals(1, binding.getInstance());
235                 return null;
236               }
237             });
238             return null;
239           }
240         }
241     );
242   }
243 
testConvertedConstantBinding()244   public void testConvertedConstantBinding() {
245     Injector injector = Guice.createInjector(new AbstractModule() {
246       protected void configure() {
247         bindConstant().annotatedWith(Names.named("one")).to("1");
248       }
249     });
250 
251     Binding<Integer> binding = injector.getBinding(Key.get(Integer.class, Names.named("one")));
252     assertEquals(Key.get(Integer.class, Names.named("one")), binding.getKey());
253     checkBindingSource(binding);
254     assertTrue(binding instanceof ConvertedConstantBinding);
255     binding.acceptTargetVisitor(new FailingTargetVisitor<Integer>() {
256       @Override public Void visit(
257           ConvertedConstantBinding<? extends Integer> binding) {
258         assertEquals((Integer) 1, binding.getValue());
259         assertEquals(Key.get(String.class, Names.named("one")), binding.getSourceKey());
260         return null;
261       }
262     });
263   }
264 
testProviderBinding()265   public void testProviderBinding() {
266     Injector injector = Guice.createInjector(new AbstractModule() {
267       protected void configure() {
268         bind(String.class).toInstance("A");
269       }
270     });
271 
272     Key<Provider<String>> providerOfStringKey = new Key<Provider<String>>() {};
273     Binding<Provider<String>> binding = injector.getBinding(providerOfStringKey);
274     assertEquals(providerOfStringKey, binding.getKey());
275     checkBindingSource(binding);
276     assertTrue(binding instanceof ProviderBinding);
277     binding.acceptTargetVisitor(new FailingTargetVisitor<Provider<String>>() {
278       @Override public Void visit(
279           ProviderBinding<? extends Provider<String>> binding) {
280         assertEquals(Key.get(String.class), binding.getProvidedKey());
281         return null;
282       }
283     });
284   }
285 
testScopes()286   public void testScopes() {
287     checkInjector(
288         new AbstractModule() {
289           protected void configure() {
290             bind(String.class).annotatedWith(Names.named("a"))
291                 .toProvider(StringProvider.class).in(Singleton.class);
292             bind(String.class).annotatedWith(Names.named("b"))
293                 .toProvider(StringProvider.class).in(Scopes.SINGLETON);
294             bind(String.class).annotatedWith(Names.named("c"))
295                 .toProvider(StringProvider.class).asEagerSingleton();
296             bind(String.class).annotatedWith(Names.named("d"))
297                 .toProvider(StringProvider.class);
298           }
299         },
300 
301         new FailingElementVisitor() {
302           @Override public <T> Void visit(Binding<T> command) {
303             assertEquals(Key.get(String.class, Names.named("a")), command.getKey());
304             command.acceptScopingVisitor(new FailingBindingScopingVisitor() {
305               @Override public Void visitScope(Scope scope) {
306                 // even though we bound with an annotation, the injector always uses instances
307                 assertSame(Scopes.SINGLETON, scope);
308                 return null;
309               }
310             });
311             return null;
312           }
313         },
314 
315         new FailingElementVisitor() {
316           @Override public <T> Void visit(Binding<T> command) {
317             assertEquals(Key.get(String.class, Names.named("b")), command.getKey());
318             command.acceptScopingVisitor(new FailingBindingScopingVisitor() {
319               @Override public Void visitScope(Scope scope) {
320                 assertSame(Scopes.SINGLETON, scope);
321                 return null;
322               }
323             });
324             return null;
325           }
326         },
327 
328         new FailingElementVisitor() {
329           @Override public <T> Void visit(Binding<T> command) {
330             assertEquals(Key.get(String.class, Names.named("c")), command.getKey());
331             command.acceptScopingVisitor(new FailingBindingScopingVisitor() {
332               @Override public Void visitEagerSingleton() {
333                 return null;
334               }
335             });
336             return null;
337           }
338         },
339 
340         new FailingElementVisitor() {
341           @Override public <T> Void visit(Binding<T> command) {
342             assertEquals(Key.get(String.class, Names.named("d")), command.getKey());
343             command.acceptScopingVisitor(new FailingBindingScopingVisitor() {
344               @Override public Void visitNoScoping() {
345                 return null;
346               }
347             });
348             return null;
349           }
350         }
351     );
352   }
353 
testExtensionSpi()354   public void testExtensionSpi() {
355     final AtomicBoolean visiting = new AtomicBoolean(false);
356 
357     final Injector injector = Guice.createInjector(new AbstractModule() {
358       protected void configure() {
359         bind(String.class).toProvider(new ProviderWithExtensionVisitor<String>() {
360           public <B, V> V acceptExtensionVisitor(BindingTargetVisitor<B, V> visitor,
361               ProviderInstanceBinding<? extends B> binding) {
362             assertSame(this, binding.getUserSuppliedProvider());
363             // We can't always check for FailingSpiTargetVisitor,
364             // because constructing the injector visits here, and we need
365             // to process the binding as normal
366             if(visiting.get()) {
367               assertTrue("visitor: " + visitor, visitor instanceof FailingSpiTargetVisitor);
368               return (V)"visited";
369             } else {
370               return visitor.visit(binding);
371             }
372           }
373 
374           public String get() {
375             return "FooBar";
376           }
377         });
378       }
379     });
380 
381     visiting.set(true);
382 
383     // Check for Provider<String> binding -- that is still a ProviderBinding.
384     Key<Provider<String>> providerOfStringKey = new Key<Provider<String>>() {};
385     Binding<Provider<String>> providerBinding = injector.getBinding(providerOfStringKey);
386     assertEquals(providerOfStringKey, providerBinding.getKey());
387     checkBindingSource(providerBinding);
388     assertTrue("binding: " + providerBinding, providerBinding instanceof ProviderBinding);
389     providerBinding.acceptTargetVisitor(new FailingTargetVisitor<Provider<String>>() {
390       @Override public Void visit(ProviderBinding<? extends Provider<String>> binding) {
391         assertEquals(Key.get(String.class), binding.getProvidedKey());
392         return null;
393       }
394     });
395 
396     // Check for String binding -- that one is ProviderInstanceBinding, and gets hooked
397     Binding<String> binding = injector.getBinding(String.class);
398     assertEquals(Key.get(String.class), binding.getKey());
399     checkBindingSource(binding);
400     assertTrue(binding instanceof ProviderInstanceBinding);
401     assertEquals("visited", binding.acceptTargetVisitor(new FailingSpiTargetVisitor<String>()));
402   }
403 
404   private static class FailingSpiTargetVisitor<T> extends DefaultBindingTargetVisitor<T, String> {
405     @Override
visitOther(Binding<? extends T> binding)406     protected String visitOther(Binding<? extends T> binding) {
407       throw new AssertionFailedError();
408     }
409   }
410 
checkBindingSource(Binding binding)411   public void checkBindingSource(Binding binding) {
412     assertContains(binding.getSource().toString(), getDeclaringSourcePart(getClass()));
413     ElementSource source = (ElementSource) binding.getSource();
414     assertFalse(source.getModuleClassNames().isEmpty());
415     if (isIncludeStackTraceComplete()) {
416       assertTrue(source.getStackTrace().length > 0);
417     } else {
418       assertEquals(0, source.getStackTrace().length);
419     }
420   }
421 
checkInjector(Module module, ElementVisitor<?>... visitors)422   public void checkInjector(Module module, ElementVisitor<?>... visitors) {
423     Injector injector = Guice.createInjector(module);
424 
425     List<Binding<?>> bindings = Lists.newArrayList(injector.getBindings().values());
426     for (Iterator<Binding<?>> i = bindings.iterator(); i.hasNext(); ) {
427       if (BUILT_IN_BINDINGS.contains(i.next().getKey())) {
428         i.remove();
429       }
430     }
431 
432     Collections.sort(bindings, orderByKey);
433 
434     assertEquals(bindings.size(), visitors.length);
435 
436     for (int i = 0; i < visitors.length; i++) {
437       ElementVisitor<?> visitor = visitors[i];
438       Binding<?> binding = bindings.get(i);
439       binding.acceptVisitor(visitor);
440     }
441   }
442 
443   private final ImmutableSet<Key<?>> BUILT_IN_BINDINGS = ImmutableSet.of(
444       Key.get(Injector.class), Key.get(Stage.class), Key.get(Logger.class));
445 
446   private final Comparator<Binding<?>> orderByKey = new Comparator<Binding<?>>() {
447     public int compare(Binding<?> a, Binding<?> b) {
448       return a.getKey().toString().compareTo(b.getKey().toString());
449     }
450   };
451 
452   private static class StringProvider implements Provider<String> {
get()453     public String get() {
454       return "A";
455     }
456   }
457 
458   private static class C { }
459 
460   private static class D extends C {
D(Injector unused)461     @Inject public D(Injector unused) { }
462   }
463 }
464