1 /*
2 Copyright (C) 2007 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.getDeclaringSourcePart;
21 import static java.lang.annotation.ElementType.TYPE;
22 import static java.lang.annotation.RetentionPolicy.RUNTIME;
23 
24 import com.google.common.collect.ImmutableList;
25 import com.google.common.collect.Iterables;
26 import com.google.inject.matcher.Matchers;
27 import com.google.inject.name.Names;
28 import com.google.inject.spi.TypeConverter;
29 
30 import junit.framework.TestCase;
31 
32 import java.lang.annotation.Retention;
33 import java.lang.annotation.Target;
34 import java.util.List;
35 
36 /**
37  * @author jessewilson@google.com (Jesse Wilson)
38  */
39 public class ParentInjectorTest extends TestCase {
40 
testParentAndChildCannotShareExplicitBindings()41   public void testParentAndChildCannotShareExplicitBindings() {
42     Injector parent = Guice.createInjector(bindsA);
43     try {
44       parent.createChildInjector(bindsA);
45       fail("Created the same explicit binding on both parent and child");
46     } catch (CreationException e) {
47       assertContains(e.getMessage(), "A binding to ", A.class.getName(), " was already configured",
48           " at ", getClass().getName(), getDeclaringSourcePart(getClass()),
49           " at ", getClass().getName(), getDeclaringSourcePart(getClass()));
50     }
51   }
52 
testParentJitBindingWontClobberChildBinding()53   public void testParentJitBindingWontClobberChildBinding() {
54     Injector parent = Guice.createInjector();
55     parent.createChildInjector(bindsA);
56     try {
57       parent.getInstance(A.class);
58       fail("Created a just-in-time binding on the parent that's the same as a child's binding");
59     } catch (ConfigurationException e) {
60       assertContains(e.getMessage(),
61           "Unable to create binding for " + A.class.getName(),
62           "It was already configured on one or more child injectors or private modules",
63           "bound at " + bindsA.getClass().getName() + ".configure(",
64           "If it was in a PrivateModule, did you forget to expose the binding?",
65           "while locating " + A.class.getName());
66     }
67   }
68 
testChildCannotBindToAParentJitBinding()69   public void testChildCannotBindToAParentJitBinding() {
70     Injector parent = Guice.createInjector();
71     parent.getInstance(A.class);
72     try {
73       parent.createChildInjector(bindsA);
74       fail();
75     } catch(CreationException ce) {
76       assertContains(Iterables.getOnlyElement(ce.getErrorMessages()).getMessage(),
77           "A just-in-time binding to " + A.class.getName() + " was already configured on a parent injector.");
78     }
79   }
80 
testJustInTimeBindingsAreSharedWithParentIfPossible()81   public void testJustInTimeBindingsAreSharedWithParentIfPossible() {
82     Injector parent = Guice.createInjector();
83     Injector child = parent.createChildInjector();
84     assertSame(child.getInstance(A.class), parent.getInstance(A.class));
85 
86     Injector anotherChild = parent.createChildInjector();
87     assertSame(anotherChild.getInstance(A.class), parent.getInstance(A.class));
88 
89     Injector grandchild = child.createChildInjector();
90     assertSame(grandchild.getInstance(A.class), parent.getInstance(A.class));
91   }
92 
testBindingsInherited()93   public void testBindingsInherited() {
94     Injector parent = Guice.createInjector(bindsB);
95     Injector child = parent.createChildInjector();
96     assertSame(RealB.class, child.getInstance(B.class).getClass());
97   }
98 
testGetParent()99   public void testGetParent() {
100     Injector top = Guice.createInjector(bindsA);
101     Injector middle = top.createChildInjector(bindsB);
102     Injector bottom = middle.createChildInjector();
103     assertSame(middle, bottom.getParent());
104     assertSame(top, middle.getParent());
105     assertNull(top.getParent());
106   }
107 
testChildBindingsNotVisibleToParent()108   public void testChildBindingsNotVisibleToParent() {
109     Injector parent = Guice.createInjector();
110     parent.createChildInjector(bindsB);
111     try {
112       parent.getBinding(B.class);
113       fail();
114     } catch (ConfigurationException expected) {
115     }
116   }
117 
testScopesInherited()118   public void testScopesInherited() {
119     Injector parent = Guice.createInjector(new AbstractModule() {
120       @Override protected void configure() {
121         bindScope(MyScope.class, Scopes.SINGLETON);
122       }
123     });
124     Injector child = parent.createChildInjector(new AbstractModule() {
125       @Override protected void configure() {
126         bind(A.class).in(MyScope.class);
127       }
128     });
129     assertSame(child.getInstance(A.class), child.getInstance(A.class));
130   }
131 
132   /*if[AOP]*/
133   private final org.aopalliance.intercept.MethodInterceptor returnNullInterceptor
134       = new org.aopalliance.intercept.MethodInterceptor() {
135     public Object invoke(org.aopalliance.intercept.MethodInvocation methodInvocation) {
136       return null;
137     }
138   };
139 
testInterceptorsInherited()140   public void testInterceptorsInherited() {
141     Injector parent = Guice.createInjector(new AbstractModule() {
142       @Override protected void configure() {
143         super.bindInterceptor(Matchers.any(), Matchers.returns(Matchers.identicalTo(A.class)),
144             returnNullInterceptor);
145       }
146     });
147 
148     Injector child = parent.createChildInjector(new AbstractModule() {
149       @Override protected void configure() {
150         bind(C.class);
151       }
152     });
153 
154     assertNull(child.getInstance(C.class).interceptedMethod());
155   }
156   /*end[AOP]*/
157 
testTypeConvertersInherited()158   public void testTypeConvertersInherited() {
159     Injector parent = Guice.createInjector(bindListConverterModule);
160     Injector child = parent.createChildInjector(bindStringNamedB);
161 
162     assertEquals(ImmutableList.of(), child.getInstance(Key.get(List.class, Names.named("B"))));
163   }
164 
testTypeConvertersConflicting()165   public void testTypeConvertersConflicting() {
166     Injector parent = Guice.createInjector(bindListConverterModule);
167     Injector child = parent.createChildInjector(bindListConverterModule, bindStringNamedB);
168 
169     try {
170       child.getInstance(Key.get(List.class, Names.named("B")));
171       fail();
172     } catch (ConfigurationException expected) {
173       Asserts.assertContains(expected.getMessage(), "Multiple converters can convert");
174     }
175   }
176 
testInjectorInjectionSpanningInjectors()177   public void testInjectorInjectionSpanningInjectors() {
178     Injector parent = Guice.createInjector();
179     Injector child = parent.createChildInjector(new AbstractModule() {
180       @Override protected void configure() {
181         bind(D.class);
182       }
183     });
184 
185     D d = child.getInstance(D.class);
186     assertSame(d.injector, child);
187 
188     E e = child.getInstance(E.class);
189     assertSame(e.injector, parent);
190   }
191 
testSeveralLayersOfHierarchy()192   public void testSeveralLayersOfHierarchy() {
193     Injector top = Guice.createInjector(bindsA);
194     Injector left = top.createChildInjector();
195     Injector leftLeft = left.createChildInjector(bindsD);
196     Injector right = top.createChildInjector(bindsD);
197 
198     assertSame(leftLeft, leftLeft.getInstance(D.class).injector);
199     assertSame(right, right.getInstance(D.class).injector);
200     assertSame(top, leftLeft.getInstance(E.class).injector);
201     assertSame(top.getInstance(A.class), leftLeft.getInstance(A.class));
202 
203     Injector leftRight = left.createChildInjector(bindsD);
204     assertSame(leftRight, leftRight.getInstance(D.class).injector);
205 
206     try {
207       top.getInstance(D.class);
208       fail();
209     } catch (ConfigurationException expected) {
210     }
211 
212     try {
213       left.getInstance(D.class);
214       fail();
215     } catch (ConfigurationException expected) {
216     }
217   }
218 
testScopeBoundInChildInjectorOnly()219   public void testScopeBoundInChildInjectorOnly() {
220     Injector parent = Guice.createInjector();
221     Injector child = parent.createChildInjector(new AbstractModule() {
222       @Override protected void configure() {
223         bindScope(MyScope.class, Scopes.SINGLETON);
224       }
225     });
226 
227     try {
228       parent.getProvider(F.class);
229       fail();
230     } catch (ConfigurationException expected) {
231       assertContains(expected.getMessage(),
232           "No scope is bound to com.google.inject.ParentInjectorTest$MyScope.",
233           "at " + F.class.getName() + ".class(ParentInjectorTest.java",
234           "  while locating " + F.class.getName());
235     }
236 
237     assertNotNull(child.getProvider(F.class).get());
238   }
239 
testErrorInParentButOkayInChild()240   public void testErrorInParentButOkayInChild() {
241     Injector parent = Guice.createInjector();
242     Injector childInjector = parent.createChildInjector(new AbstractModule() {
243       @Override protected void configure() {
244         bindScope(MyScope.class, Scopes.SINGLETON);
245         bind(Object.class).to(F.class);
246       }
247     });
248     Object one = childInjector.getInstance(Object.class);
249     Object two = childInjector.getInstance(Object.class);
250     assertSame(one, two);
251   }
252 
testErrorInParentAndChild()253   public void testErrorInParentAndChild() {
254     Injector parent = Guice.createInjector();
255     Injector childInjector = parent.createChildInjector();
256 
257     try {
258       childInjector.getInstance(G.class);
259       fail();
260     } catch(ConfigurationException expected) {
261       assertContains(expected.getMessage(), "No scope is bound to " + MyScope.class.getName(),
262           "at " + F.class.getName() + ".class(ParentInjectorTest.java:",
263           "  while locating " + G.class.getName());
264     }
265   }
266 
267   @Singleton
268   static class A {}
269 
270   private final Module bindsA = new AbstractModule() {
271     @Override protected void configure() {
272       bind(A.class).toInstance(new A());
273     }
274   };
275 
276   interface B {}
277   static class RealB implements B {}
278 
279   private final Module bindsB = new AbstractModule() {
280     @Override protected void configure() {
281       bind(B.class).to(RealB.class);
282     }
283   };
284 
285   @Target(TYPE) @Retention(RUNTIME) @ScopeAnnotation
286   public @interface MyScope {}
287 
288   private final TypeConverter listConverter = new TypeConverter() {
289     public Object convert(String value, TypeLiteral<?> toType) {
290       return ImmutableList.of();
291     }
292   };
293 
294   private final Module bindListConverterModule = new AbstractModule() {
295     @Override protected void configure() {
296       convertToTypes(Matchers.any(), listConverter);
297     }
298   };
299 
300   private final Module bindStringNamedB = new AbstractModule() {
301     @Override protected void configure() {
302       bind(String.class).annotatedWith(Names.named("B")).toInstance("buzz");
303     }
304   };
305 
306   public static class C {
interceptedMethod()307     public A interceptedMethod() {
308       return new A();
309     }
310   }
311 
312   static class D {
313     @Inject Injector injector;
314   }
315 
316   static class E {
317     @Inject Injector injector;
318   }
319 
320   private final Module bindsD = new AbstractModule() {
321     @Override protected void configure() {
322       bind(D.class);
323     }
324   };
325 
326   @MyScope
327   static class F implements G {}
328 
329   @ImplementedBy(F.class)
330   interface G {}
331 }
332